tag:blogger.com,1999:blog-42083784820212199222024-03-16T04:30:19.917-07:00Aspiring ArchitectPratap Reddy Pilakahttp://www.blogger.com/profile/17141976956878163883noreply@blogger.comBlogger145125tag:blogger.com,1999:blog-4208378482021219922.post-56033805024986197152023-11-27T02:08:00.000-08:002023-11-27T02:09:49.203-08:00Azure FinOps using Terraform and Infracost - Finding the hourly or monthly cost before Azure DevOps DeploymentsA while ago i created a demo for Azure VWAN using terraform and Azure DevOps. I dive headfirst without realizing that i am using premium SKU for firewalls and my Dev teant is shutdown for a month in few days due to my billing cap of 230 NZD.<div><br /></div><div>Next time when i create a demo for APIM instances, i dint realize Premium SKU costs 7500 NZD/month. Even before i finish my POC, the teant again shutdown in few hours this time.</div><div><br /></div><div>Our objective is to find the cost of the IAC we are deploying even before we deploy. </div><div>In this post i will show how we can utilize Infracost a opensource plugin in both VSCode and how we can make it part of our Azure DevOps pipelines to manage cost of the resources we are going to deploy.</div><div><br /></div><div>Like this :</div><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi9yjOhjkQf_fCmIIFeOJmwb3NOtwLa8sy1kthdWomuDKaLqsUktGKeh-Lf5T7Wcc4M-70gUI9AnVZEhctNJc5_2Zt7AOr2C9lmP7r93VKQz5KN3Xe_6imOeZ7a9sMJ-51uUzfZPAjLbBhOYq23OeVhQVWitKwbbTIF15-xukdnaC9fhSIABQ129uibt_c/s1567/1.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="937" data-original-width="1567" height="382" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi9yjOhjkQf_fCmIIFeOJmwb3NOtwLa8sy1kthdWomuDKaLqsUktGKeh-Lf5T7Wcc4M-70gUI9AnVZEhctNJc5_2Zt7AOr2C9lmP7r93VKQz5KN3Xe_6imOeZ7a9sMJ-51uUzfZPAjLbBhOYq23OeVhQVWitKwbbTIF15-xukdnaC9fhSIABQ129uibt_c/w640-h382/1.jpg" width="640" /></a></div></div><div><br /></div><div>or like this:</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjRupoBBPc5oltmhtmlqnezWS4Iq-AlwErvhzTM-z9cDE7qVEk0thVPm5KaJy7peAJ0xkSbfI3U2cuRRd1nHx1z1ISXl4qZetBDJWIVX2MZV-___ZBB0vEFWTVF5rgSlpd-a9B7F5P_iZ3yj8rn-gLrcRz7-DeobXN9oIBWSp4GRQsCQZlCaIpFkDgpi8U/s1033/2.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="670" data-original-width="1033" height="416" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjRupoBBPc5oltmhtmlqnezWS4Iq-AlwErvhzTM-z9cDE7qVEk0thVPm5KaJy7peAJ0xkSbfI3U2cuRRd1nHx1z1ISXl4qZetBDJWIVX2MZV-___ZBB0vEFWTVF5rgSlpd-a9B7F5P_iZ3yj8rn-gLrcRz7-DeobXN9oIBWSp4GRQsCQZlCaIpFkDgpi8U/w640-h416/2.png" width="640" /></a></div><a name='more'></a><br /><h3 style="text-align: left;">VS Code:</h3><div>To use Infracost in VSCode, go to <a href="http://infracost.io">infracost.io</a> and download the Infracost.exe and place it in c:\windows folder.</div><div><br /></div><div>As soon as you did above step, reopen your VS Code and you can start seing the cost of individual components as shown above. But we want to see the cost of the whole deployment, not just resource by resource. </div><div><br /></div><div>Now either use your own code base, or you can download my <a href="https://github.com/pratappilaka24/Azure-VWAN" target="_blank">AzureVWAN</a> codebase from my GitRepo. Dont worry i chnaged the SKUs to basic and we are not deploying anything. Our objective is to find cost even before deploying.</div><div><br /></div><div>Open your TF source code in VS Code and open terminal and runn your tfplan command as shown below:</div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhMCLGq9e-uQ6XntF3UMk_PsIg9T6wqW-XlZrC-GNcqu_Gm-z_dg1C9pQcSybuvRlazDODco1tYWyc_JchoKTZl3urk1M7_o4GW4-b20xhnmDqcWcDkYbj2lQHKgS-G26nL6IFnso1mPdAzDVffwfgXoYqLBcWqGLKr2tAmQ4K7Z0aYnWJZbkeXhjegdns/s978/4.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="55" data-original-width="978" height="36" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhMCLGq9e-uQ6XntF3UMk_PsIg9T6wqW-XlZrC-GNcqu_Gm-z_dg1C9pQcSybuvRlazDODco1tYWyc_JchoKTZl3urk1M7_o4GW4-b20xhnmDqcWcDkYbj2lQHKgS-G26nL6IFnso1mPdAzDVffwfgXoYqLBcWqGLKr2tAmQ4K7Z0aYnWJZbkeXhjegdns/w640-h36/4.png" width="640" /></a></div><br />We need tfplan output into a file, because in VS Code the cost is generated based on the plan. Above command will generate the plan output into a file in current working folder.</div><div><br /></div><div>Now run below command to authenticate InfraCost.</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiulrIhyn6HcJnznjTVEgD_TSjzMX14B5m35PVPqqIZR-cAXvHred6EN_qiaJRPB8Jm3pDKfGyG3VHcKRIWb_JodLI6FWuaTjDhZJyCF9-V6FQc45zeFgINVzGYdIvvUwNujk2Q9sybMFEOA4Tpj0RquhcSjL0TFq5-4iWf1rn6bEPeJ0cyy60dzLXO95Q/s315/5.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="70" data-original-width="315" height="70" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiulrIhyn6HcJnznjTVEgD_TSjzMX14B5m35PVPqqIZR-cAXvHred6EN_qiaJRPB8Jm3pDKfGyG3VHcKRIWb_JodLI6FWuaTjDhZJyCF9-V6FQc45zeFgINVzGYdIvvUwNujk2Q9sybMFEOA4Tpj0RquhcSjL0TFq5-4iWf1rn6bEPeJ0cyy60dzLXO95Q/s1600/5.png" width="315" /></a></div><br /><div>This will open the web browser for authentication with your Infracost account. This will autamtically get the API token from Infracost and save it in VScode settings. </div><div><br /></div><div>Now run below command :</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhF7WYxed1ALyso1smDDJUMxjsqcLlrPlPbDaO8e_aitKRYWwTG9uPpD05nGRZm2OhcG9fAIS9UfXW-CS41869TAp7Ue0HGFhnmwFreEzC0l9QG0g-x3pitIPW_Pi5DpN4JarIuJ2WUvAJUTNomemcopKJC8-M2tBJ2OdTMApq1D0P1U0wuqSMMmxQgoo4/s403/6.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="46" data-original-width="403" height="37" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhF7WYxed1ALyso1smDDJUMxjsqcLlrPlPbDaO8e_aitKRYWwTG9uPpD05nGRZm2OhcG9fAIS9UfXW-CS41869TAp7Ue0HGFhnmwFreEzC0l9QG0g-x3pitIPW_Pi5DpN4JarIuJ2WUvAJUTNomemcopKJC8-M2tBJ2OdTMApq1D0P1U0wuqSMMmxQgoo4/s320/6.png" width="320" /></a></div><br /><div>This will utilize the plan file generated earlier and will give you cost of each component and final mothly cost of your deployment as shown below.</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgXQ4LCFZYHOGKgbUUDQaQeqhPJI8faSc4GLxI9ZWMtxrnTAD3sI0EjJPThmAcEXc1ITvtWUzngrsjPgaZHQk6up5bSmGnmm52n35UkyOX4aCkNziBNJjZuvAl34h4_93S8dsSLjHwMOoC8wcx2eublYzjwPTcDdlRf4hmOL2dfuQzjlJMmYEIgLqTNNgk/s1567/1.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="937" data-original-width="1567" height="382" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgXQ4LCFZYHOGKgbUUDQaQeqhPJI8faSc4GLxI9ZWMtxrnTAD3sI0EjJPThmAcEXc1ITvtWUzngrsjPgaZHQk6up5bSmGnmm52n35UkyOX4aCkNziBNJjZuvAl34h4_93S8dsSLjHwMOoC8wcx2eublYzjwPTcDdlRf4hmOL2dfuQzjlJMmYEIgLqTNNgk/w640-h382/1.jpg" width="640" /></a></div><br /><div>When you are doing a multiple versions of build, you can use below command to find the difference of the costs based on your base build. <a href="https://www.infracost.io/docs/#4-show-cost-estimate-diff" target="_blank">More on this here</a>.</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEglg7fEaSN4slU-XzhRmh8BepcLAOa7r2Upz4HPK2ndG2KxH0mdnpCjZROzkChm-Bl0sUWxGctoBfW3DTHaqp-IosjOhUhHZzjzNNxg2ccIKeGPHpi6q8HEAZSF4HIahQOZ_q7A9_7-Z7tN_nZx7GA1y2poOlq4kYnV7N0jesC2Cwcz-V26sR8xa0lzVK4/s709/7.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="73" data-original-width="709" height="41" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEglg7fEaSN4slU-XzhRmh8BepcLAOa7r2Upz4HPK2ndG2KxH0mdnpCjZROzkChm-Bl0sUWxGctoBfW3DTHaqp-IosjOhUhHZzjzNNxg2ccIKeGPHpi6q8HEAZSF4HIahQOZ_q7A9_7-Z7tN_nZx7GA1y2poOlq4kYnV7N0jesC2Cwcz-V26sR8xa0lzVK4/w400-h41/7.png" width="400" /></a></div><div><br /></div><h3 style="text-align: left;">Azure DevOps Pipeline</h3><div>Unlike we cannot use interactive authentication in middle of your devOps pieline execution, we need a API token to authenticate Infracost API.</div><div><br /></div><div>Once you done with Infracost.exe, signup on Infracost.io using your Github account or gmail account. Its free, opensource. Finishing signup it will send a Access token(Infracostaccesstoken) to your email Else log onto InfraCost.io and go to Organization settings and you can reteive your API key. </div><div><br /></div><div>Go to your Azure DevOps organization settings and install InfraCost extension. If it is not installed earlier, go to "Browse marketplace".</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjaiKb29p0Pxtvsi1KsSxaTk8e14e53E1WoN6SwNk5oiFoxmVemJA8CofraVpDrdVrkUkr-QC-G6dHZsOdoAmEsMgEwQwZ03AANuDm7YsAHeZJWjQ4zD39uxQwr0MjgHfK3ElCKUwoLcaEV8YoMeUbUvzAeDPNTLJ9744YnH2ONyWzvz2XMLrVoqN0zcBo/s1654/8.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="762" data-original-width="1654" height="294" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjaiKb29p0Pxtvsi1KsSxaTk8e14e53E1WoN6SwNk5oiFoxmVemJA8CofraVpDrdVrkUkr-QC-G6dHZsOdoAmEsMgEwQwZ03AANuDm7YsAHeZJWjQ4zD39uxQwr0MjgHfK3ElCKUwoLcaEV8YoMeUbUvzAeDPNTLJ9744YnH2ONyWzvz2XMLrVoqN0zcBo/w640-h294/8.png" width="640" /></a></div><br /><div>then</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjty-wRRC3ToE9Mnaf79nMY0SmL0fLcWkXv6bLRTpQYDiHQzMWUcDOctHbtJbhSv0qfZI5bNPnDtkkyq6o2tVRnanZb7tQu26zfG6Fbo1Y88xvLOpHpWOZETzEba1-Nq4EVXO4v_Y83tPfnGc4PR1XqIGT_YZmaRcHybvuU2obq8Gvs5V9uEcFzLOTF3l0/s1042/9.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="745" data-original-width="1042" height="458" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjty-wRRC3ToE9Mnaf79nMY0SmL0fLcWkXv6bLRTpQYDiHQzMWUcDOctHbtJbhSv0qfZI5bNPnDtkkyq6o2tVRnanZb7tQu26zfG6Fbo1Y88xvLOpHpWOZETzEba1-Nq4EVXO4v_Y83tPfnGc4PR1XqIGT_YZmaRcHybvuU2obq8Gvs5V9uEcFzLOTF3l0/w640-h458/9.png" width="640" /></a></div><br /><div><br /></div><div>Now take any existing Repo on Azure Devops, I am using the same AzureVWAN project on my devops repos.</div><div>Create a new pipeline => Azure repos Git => select current project => Starter Pipeline.</div><div><br /></div><div>Copy paste below code:</div><div><br /></div><div><span style="background-color: #fffffe; color: #007200; font-family: Consolas, "Courier New", monospace; font-size: 14px; white-space: pre;"># Starter pipeline</span></div><div style="background-color: #fffffe; font-family: Consolas, "Courier New", monospace; font-size: 14px; line-height: 19px; white-space: pre;"><div><span style="color: #007200;"># Start with a minimal pipeline that you can customize to build and deploy your code.</span></div><div><span style="color: #007200;"># Add steps that build, run tests, deploy, and more:</span></div><div><span style="color: #007200;"># https://aka.ms/yaml</span></div><br /><div><span style="color: teal;">trigger</span>:</div><div>- <span style="color: #0451a5;">main</span></div><br /><div><span style="color: teal;">pool</span>:</div><div> <span style="color: teal;">vmImage</span>: <span style="color: #0451a5;">ubuntu-latest</span></div><br /><div><span style="color: teal;">stages</span>:</div><br /><div><span style="color: #007200;">########### Prod Deployment ########### </span></div><div> - <span style="color: teal;">stage</span>: <span style="color: #0451a5;">"terraform_cost_plan"</span></div><div> <span style="color: teal;">displayName</span>: <span style="color: #0451a5;">"terraform-cost-plan"</span></div><div> <span style="color: teal;">jobs</span>:</div><div> - <span style="color: teal;">job</span>: <span style="color: #0451a5;">infracost</span></div><div> <span style="color: teal;">displayName</span>: <span style="color: #0451a5;">Run Infracost </span></div><div> <span style="color: teal;">steps</span>:</div><div> <span style="color: #007200;"># If you use private modules, add a base 64 encoded secret</span></div><div> <span style="color: #007200;"># called gitSshKeyBase64 with your private key, so Infracost can access</span></div><div> <span style="color: #007200;"># private repositories (similar to how Terraform/Terragrunt does).</span></div><div> <span style="color: #007200;"># - bash: |</span></div><div> <span style="color: #007200;"># ssh-agent -a $(SSH_AUTH_SOCK)</span></div><div> <span style="color: #007200;"># mkdir -p ~/.ssh</span></div><div> <span style="color: #007200;"># echo "$(echo $GIT_SSH_KEY_BASE_64 | base64 -d)" | tr -d '\r' | ssh-add -</span></div><div> <span style="color: #007200;"># # Update this to github.com, gitlab.com, bitbucket.org, ssh.dev.azure.com or your source control server's domain</span></div><div> <span style="color: #007200;"># ssh-keyscan github.com >> ~/.ssh/known_hosts</span></div><div> <span style="color: #007200;"># displayName: Add GIT_SSH_KEY</span></div><div> <span style="color: #007200;"># env:</span></div><div> <span style="color: #007200;"># GIT_SSH_KEY_BASE_64: $(gitSshKeyBase64)</span></div><br /><div> <span style="color: #007200;"># Install the Infracost CLI, see https://github.com/infracost/infracost-azure-devops#infracostsetup</span></div><div> <span style="color: #007200;"># for other inputs such as version, and pricingApiEndpoint (for self-hosted users).</span></div><div> - <span style="color: teal;">task</span>: <span style="color: #0451a5;">InfracostSetup@2</span></div><div> <span style="color: teal;">displayName</span>: <span style="color: #0451a5;">Setup Infracost</span></div><div> <span style="color: teal;">inputs</span>:</div><div> <span style="color: teal;">apiKey</span>: <span style="color: #0451a5;">$(infracostApiKey)</span></div><br /><div> <span style="color: #007200;"># Clone the base branch of the pull request (e.g. main/master) into a temp directory.</span></div><div> - <span style="color: teal;">bash</span>: |</div><div><span style="color: #0451a5;"> branch=$(System.PullRequest.TargetBranch)</span></div><div><span style="color: #0451a5;"> branch=${branch#refs/heads/}</span></div><div><span style="color: #0451a5;"> git clone $(Build.Repository.Uri) --branch="main" --single-branch /tmp/base --config http.extraheader="AUTHORIZATION: bearer $(System.AccessToken)"</span></div><div> <span style="color: teal;">displayName</span>: <span style="color: #0451a5;">Checkout base branch</span></div><br /><br /><div> <span style="color: #007200;"># Generate an Infracost cost estimate baseline from the comparison branch, so that Infracost can compare the cost difference.</span></div><div> - <span style="color: teal;">bash</span>: |</div><div><span style="color: #0451a5;"> infracost breakdown --path=/tmp/base/$(TF_ROOT) \</span></div><div> <span style="color: #0451a5;">--format=json \</span></div><div> <span style="color: #0451a5;">--out-file=/tmp/infracost-base.json</span></div><div> <span style="color: teal;">displayName</span>: <span style="color: #0451a5;">Generate Infracost cost estimate baseline</span></div><br /><div> - <span style="color: teal;">bash</span>: |</div><div><span style="color: #0451a5;"> sudo apt install jq</span></div><div> <span style="color: teal;">displayName</span>: <span style="color: #0451a5;">Installing JQ to prettyfy output</span></div><br /><div> - <span style="color: teal;">bash</span>: |</div><div><span style="color: #0451a5;"> jq . /tmp/infracost-base.json</span></div><div> <span style="color: teal;">displayName</span>: <span style="color: #0451a5;">Printing Baseline cost estimate</span></div><br /><div> <span style="color: #007200;"># Generate an Infracost diff and save it to a JSON file.</span></div><div> - <span style="color: teal;">bash</span>: |</div><div><span style="color: #0451a5;"> infracost diff --path=$(TF_ROOT) \</span></div><div> <span style="color: #0451a5;">--format=json \</span></div><div> <span style="color: #0451a5;">--compare-to=/tmp/infracost-base.json \</span></div><div> <span style="color: #0451a5;">--out-file=/tmp/infracost.json</span></div><div> <span style="color: teal;">displayName</span>: <span style="color: #0451a5;">Generate Infracost diff</span></div><br /><div> - <span style="color: teal;">bash</span>: |</div><div><span style="color: #0451a5;"> jq . /tmp/infracost-base.json</span></div></div><div><tt><span style="background-color: #fffffe; font-family: Consolas, "Courier New", monospace; font-size: 14px; white-space: pre;"> </span><span style="background-color: #fffffe; color: teal; font-family: Consolas, "Courier New", monospace; font-size: 14px; white-space: pre;">displayName</span><span style="background-color: #fffffe; font-family: Consolas, "Courier New", monospace; font-size: 14px; white-space: pre;">: </span><span style="background-color: #fffffe; color: #0451a5; font-family: Consolas, "Courier New", monospace; font-size: 14px; white-space: pre;">Printing cost difference estimate</span></tt></div><div><br /></div><div><br /></div><div>Before you save the pipeline, click varaibles button on top right corner and add a variable with name "infracostapikey" and the value should be the API key you saved/generated/copied earlier from infracost organization settings.</div><div><br /></div><div>Now save and run the pipeline.</div><div>Boom: </div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiet2cihOttqLBrRi7UolgGXK9owKmASIOGTemoN5SDQCOzN4Imd3aPhMDJBk47eVqEiyjw8YamTZZlGakY7KnlOh402rAjqfRMI-c7PZmRmL3RJVvBcjK1qqAbMV7ymJLA7UoyMvAIT8ePDzpL8uq_WgfGlXwD0pML_8SlQ9LKZ61TKmRAp2gXRjsQsfE/s1770/10.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1330" data-original-width="1770" height="480" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiet2cihOttqLBrRi7UolgGXK9owKmASIOGTemoN5SDQCOzN4Imd3aPhMDJBk47eVqEiyjw8YamTZZlGakY7KnlOh402rAjqfRMI-c7PZmRmL3RJVvBcjK1qqAbMV7ymJLA7UoyMvAIT8ePDzpL8uq_WgfGlXwD0pML_8SlQ9LKZ61TKmRAp2gXRjsQsfE/w640-h480/10.png" width="640" /></a></div><br /><div>I have even embedded the code to give you the diff next time when you run with chnages to your build.</div><div><br /></div><div>You can make this pipeline as a one of the build validation task everytime when main or master branch was updated and can create chnage management / goverance / FinOps arround your Azure DevOps deployments. </div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /><div><br /><div><br /></div></div></div>Pratap Reddy Pilakahttp://www.blogger.com/profile/17141976956878163883noreply@blogger.com0tag:blogger.com,1999:blog-4208378482021219922.post-48509059557115660092023-07-16T01:11:00.005-07:002023-07-16T01:31:15.203-07:00Azure DevOps Self-Hosted Agents Automation Using Packer and Terrafrom<div><div>In Azure DevOps, "self-hosted agents" refer to agent machines that you set up and manage yourself, instead of using Microsoft-hosted agents. These self-hosted agents can be beneficial in various scenarios:</div><div><br /></div><div><b><span> </span>Security and Compliance:</b> In some organizations, data security and compliance policies may require running build and deployment processes on infrastructure managed within their own network.</div><div><br /></div><div><b><span> </span>Access to Internal Resources:</b> Your build and deployment processes may require access to internal resources (databases, network drives, etc.) that are not accessible from external Microsoft-hosted agents.</div><div><br /></div><div><b><span> </span>Performance and Customization: </b>Self-hosted agents can be tailored to specific hardware configurations, which might be necessary for resource-intensive builds or specialized build environments.</div><div><br /></div><div><b><span> </span>Cost Management:</b> Azure DevOps provides a certain number of free Microsoft-hosted parallel jobs, but if you have large-scale or resource-intensive projects, self-hosted agents can be more cost-effective in the long run.</div><div><br /></div><div><b><span> </span>Reducing Build Queue Times:</b> When using Microsoft-hosted agents, you share resources with other users, which might result in longer build queue times. Self-hosted agents allow you to control the resources dedicated to your builds, potentially reducing waiting times.</div><div><br /></div><div><b><span> </span>Offline Environments:</b> If you have environments without continuous internet access, self-hosted agents can be used to facilitate builds and deployments within those isolated networks.</div></div><div><br /></div><div>In this post i will be:</div><div><ol style="text-align: left;"><li>Generating a Managed VM Image using Packer.</li><li>Saving the Managed image to a Image gallery within my tenant.</li><li>Create a Virtual Machine Scale Set(VMSS) from the said image.</li><li>Register the VMSS as DevOps Self-Hosted agents.</li><li>Run a time intensive project using self-hosted pool to see how VMSS will autoscale.</li><li>Then update the VM image with new build and see how we can update the existing Self-hosted agents.</li></ol></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgX_ffJ7laHrnuFU4q_GfRFvRSV7balOWvQWQWZWm8_fMNPo8gJA4Nv6_jFa_AV0C3MTLduvm7pkYlOh2WGp7RuYQM1slvyOQV4Ljr4fW_vRu3g4OhUTWoFM_D5QJX6rz2A_omdPg3MVVXvVY9u_USgERmzU3DLu1geCg_wIEmU9klkihhR79FF7M2Detk/s1915/1.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="933" data-original-width="1915" height="312" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgX_ffJ7laHrnuFU4q_GfRFvRSV7balOWvQWQWZWm8_fMNPo8gJA4Nv6_jFa_AV0C3MTLduvm7pkYlOh2WGp7RuYQM1slvyOQV4Ljr4fW_vRu3g4OhUTWoFM_D5QJX6rz2A_omdPg3MVVXvVY9u_USgERmzU3DLu1geCg_wIEmU9klkihhR79FF7M2Detk/w640-h312/1.jpg" width="640" /></a></div><br /><div><br /></div><a name='more'></a><div><b>Step 1:</b> Download the code from <a href="https://github.com/pratappilaka24/SelfHosted-Devops-Agent-TF-and-Packer" style="color: red;" target="_blank">SelfHosted-Devops-Agent-TF-and-Packer</a></div><div>Please know that the Linux custom Packer Image definition is not part of this repo. but dont worry, it will download all the required packer file and necessary installer scripts and packages automatically when you run the pipeline.</div><div>Below image will explain the contents of the repo.</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiTki2N8MHYuZVV9e1dwIFIhYx54dXUiDA1lbdZmTFWulURfsT4bLDo9wNPR7vkxUl6yo71nmNa5JJsZw5yBbD20dgr33sn8RebqO0mS-_I94MVPPZz7-rWGADzf1vqG5ShJYHNSIzKtl_jHE4nMGUS6qKr1K0qU3gmZ5cZt31CFjhFV-tzcaDrkhnoEyo/s2417/2.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1181" data-original-width="2417" height="312" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiTki2N8MHYuZVV9e1dwIFIhYx54dXUiDA1lbdZmTFWulURfsT4bLDo9wNPR7vkxUl6yo71nmNa5JJsZw5yBbD20dgr33sn8RebqO0mS-_I94MVPPZz7-rWGADzf1vqG5ShJYHNSIzKtl_jHE4nMGUS6qKr1K0qU3gmZ5cZt31CFjhFV-tzcaDrkhnoEyo/w640-h312/2.jpg" width="640" /></a></div><br /><div><b>Step 2: </b>Update the Suscription id in the Setup.azcli script and run it. The script will give a final out put where you need to create DevOps library variable group based on the output values. More details are there in Readme file. Please fillow the variable names defined in readme file and the values can be found from out put of above setup script.</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj_GkA1URTLtxiPypCZVDzoVf7YdC1UCj0lUWXwgzsaZ2UG_YLONDoJme_IQSoTiPzKtgKEvxI1E5lLnejbC_UuZoZIPjfSDq_O-3Nh24e5hRa5M5blREY9_hz2TLMDp5em83TXKJ_ALiJ4MNuMqguYE-zHON6_YUxVD-n1OmPQan03ZjfQa9GtjWi8Ilw/s1165/3.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1000" data-original-width="1165" height="550" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj_GkA1URTLtxiPypCZVDzoVf7YdC1UCj0lUWXwgzsaZ2UG_YLONDoJme_IQSoTiPzKtgKEvxI1E5lLnejbC_UuZoZIPjfSDq_O-3Nh24e5hRa5M5blREY9_hz2TLMDp5em83TXKJ_ALiJ4MNuMqguYE-zHON6_YUxVD-n1OmPQan03ZjfQa9GtjWi8Ilw/w640-h550/3.jpg" width="640" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><br /></div><b>Step 3:</b> Create Variables with values shown above in your DevOps project's Variable group.<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgPosGEOHtM_C_IixwAgBFm3f1ktko5d4Cyvgt9FBYg8qSYd5Dg8pclZnKV1Yxpx1AzibEiCL3Vye1hMrBEUxBlA6yoNa6iOXlbVagvRnkZ43-4rX4ues0xg1ElVeJNcfY_dXZHakscduv4vSczY27_9ua4mEqb602wFnmitzS-iBBEG6Y661oihAuOwko/s1683/4.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1683" data-original-width="1630" height="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgPosGEOHtM_C_IixwAgBFm3f1ktko5d4Cyvgt9FBYg8qSYd5Dg8pclZnKV1Yxpx1AzibEiCL3Vye1hMrBEUxBlA6yoNa6iOXlbVagvRnkZ43-4rX4ues0xg1ElVeJNcfY_dXZHakscduv4vSczY27_9ua4mEqb602wFnmitzS-iBBEG6Y661oihAuOwko/w620-h640/4.jpg" width="620" /></a></div><br /><div><b>Step 4:</b> Now create a pipeline and point it to "buildagent-generation-template.yml" and run it.</div><div>Please know that i have used only a subset of packages from ones listed in this file <a href="https://github.com/pratappilaka24/SelfHosted-Devops-Agent-TF-and-Packer/blob/master/helper/List%20of%20Install%20Artifacts.pdf">LINK</a><br /><div><br /></div><div>Ususally this subet takes 45 mins to finish. If you want to build VM with full list of installer artifacts, try using "buildagent-generation-template-full.yml" instead. This will take 1.5 hours to finish creating VMSS.</div><div><br /></div><div>Now try running the pipeline, and you will see below prompts.</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjpYZ4QX3_MDnEdXYl9PQnj4Da5xI8t62tVL_ScB8Y-xSWCsl7YmXr-I-FmIV3qaioAfO8jdns9tscIoFubRv1YPbF5peUaDOdwmMkncH4lgcMOuvhkZWXs1K31cDSeGjLZElS04K3w4lVN4cGCia8TEZhpP1FKc9szmLR3RIPPGKvlfFO7isc9helwP_Y/s1441/5.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1441" data-original-width="1398" height="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjpYZ4QX3_MDnEdXYl9PQnj4Da5xI8t62tVL_ScB8Y-xSWCsl7YmXr-I-FmIV3qaioAfO8jdns9tscIoFubRv1YPbF5peUaDOdwmMkncH4lgcMOuvhkZWXs1K31cDSeGjLZElS04K3w4lVN4cGCia8TEZhpP1FKc9szmLR3RIPPGKvlfFO7isc9helwP_Y/w620-h640/5.jpg" width="620" /></a></div><br /><div><b>Step 5:</b> Now run it for creating a VMSS and then for Updating existing VMSS based on your packer image build.</div><div><br /></div><div><blockquote><i>Please note that, in this process a temporary resource group will be created with the build number and a VM with Public IP will be created. Now the Packer will download all the installer artifacts and install them and build the VM to gold standard. Now the .vhd file will be generated and saved to the storage account. Once this process is sucessfully finished, the temporary resource group will be deleted with all the content of it. This is the part reason why we gave SPN contributir access at subscription.</i></blockquote></div><div> </div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh1IXuZHJPRO7RbWLw9cRmmlEQ2WHT5e9_p5QZvZgC_1rPlBVimZVmpPYv6ZTLGC5O0zZY86zhZpQQFvnZLM0ZNg1li_f5wKUmNbLCEG2XDhKHbtp-WUw_N_y_QAstk1rL4GYGkpRLbY-XIq1ING7Zn7u7QfwJMSmRWDkhFqLXG7L_XeF0iY-qH3edpmmQ/s1744/6.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="847" data-original-width="1744" height="310" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh1IXuZHJPRO7RbWLw9cRmmlEQ2WHT5e9_p5QZvZgC_1rPlBVimZVmpPYv6ZTLGC5O0zZY86zhZpQQFvnZLM0ZNg1li_f5wKUmNbLCEG2XDhKHbtp-WUw_N_y_QAstk1rL4GYGkpRLbY-XIq1ING7Zn7u7QfwJMSmRWDkhFqLXG7L_XeF0iY-qH3edpmmQ/w640-h310/6.jpg" width="640" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><br /></div><div>The resource group hosting VMSS will host other componenets required for VMSS, like a load balancer, a VNET public IP for load balancer and VMSS.</div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgvO8FkB22n0TjRroLiPjEUXyyq6pWO-S74CbyJ0ZShV9DxSu55B_qYgNjCLh_A5EsYZkV60fQRu5ILc3yM7Blvjnf2thVwYxxWmUDHBF5vLHyRFVhZ-w80-cGbCM70edZS76cfFrr9kztbB21zGjHgzd3XVNx4oy-77ILWtddB7A6mFyGml0ndW52BCiI/s2826/8.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="841" data-original-width="2826" height="190" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgvO8FkB22n0TjRroLiPjEUXyyq6pWO-S74CbyJ0ZShV9DxSu55B_qYgNjCLh_A5EsYZkV60fQRu5ILc3yM7Blvjnf2thVwYxxWmUDHBF5vLHyRFVhZ-w80-cGbCM70edZS76cfFrr9kztbB21zGjHgzd3XVNx4oy-77ILWtddB7A6mFyGml0ndW52BCiI/w640-h190/8.jpg" width="640" /></a></div><br /> </div><div><b>Step7:</b> Now its time to see the VMSS in action. to do that go to DevOps organization settings and navigate to "Agent Pools".</div><div><br /></div><div>Create a new agent pool and select the pool type as VMSS and authorize the Subscription and select the VMSS we created earlier. In settings i configured to have only 1 instance to be on standby and in case of any new jobs kicked in, the maximum number of instances to be 5. I also configured to shred the extra idle instances after 5 mins. In prod, this will be like 30 mins.</div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhcTnoNrbB__NuPrNtLja0msv948vv-rIDb2rWfWZeL66ap0tIuApQ8ebNTnv8ND_pmasXgXNvkfGhSHRZ8fHfGWrR60aWjfF_ZC_wD9kUihxE-TWHzz-Mt5w2QoI6G2b0BzaubKlPA_VNzkgSHp1Fvt3A7xTQebtBf46K-xsfYPA8Vais6VhmvjU5T9og/s1072/12.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="783" data-original-width="1072" height="468" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhcTnoNrbB__NuPrNtLja0msv948vv-rIDb2rWfWZeL66ap0tIuApQ8ebNTnv8ND_pmasXgXNvkfGhSHRZ8fHfGWrR60aWjfF_ZC_wD9kUihxE-TWHzz-Mt5w2QoI6G2b0BzaubKlPA_VNzkgSHp1Fvt3A7xTQebtBf46K-xsfYPA8Vais6VhmvjU5T9og/w640-h468/12.jpg" width="640" /></a></div><br /> </div><div>Now you will see the VMSS instances in the list of agents. If you couldnt, go to VMSS instances and select the instances and click upgrade. This will automatically run the required bash commands to make these VMSS instances avialable as agents.</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg0yLwvaqI0EVHTKlFMrAYdhdG2REShyThpULpjzkGaBgGB2JL6szUdelrMLA2PSVoPbWJu3yqdaFYRhmcnybs34ZglykYZ9jNdPcZZSu8-zUDUuZxcvbg755qq8r0xl9C3h-8KCKJf3OmqBaFb0Jqq5HMxPB3hmiecickOgOJW8Q1ZLRJLZFH_ZjkRsnI/s1876/9.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="555" data-original-width="1876" height="190" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg0yLwvaqI0EVHTKlFMrAYdhdG2REShyThpULpjzkGaBgGB2JL6szUdelrMLA2PSVoPbWJu3yqdaFYRhmcnybs34ZglykYZ9jNdPcZZSu8-zUDUuZxcvbg755qq8r0xl9C3h-8KCKJf3OmqBaFb0Jqq5HMxPB3hmiecickOgOJW8Q1ZLRJLZFH_ZjkRsnI/w640-h190/9.jpg" width="640" /></a></div><br /><div><br /></div><div>Now you can see that as per our configuration there will be only one devops agent instance of VMSS available on standby mode. </div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhyXj0UiQEfk2WQxW9ckRApDSvNwGNqmOyzYmV1sdXMt9otCbtEFykO5mYq6HWsMFM6yr5i1C18jrCmBtWUQrhPnb1E-QamJX1a_XYiyEGUYz6xyWU-ymYMLHzEja7QXj0PnYepIUdMwmwUxTIr7gpJliCATU8Q1XmcrUHGjVrqd2bciTLImiZTSrM0Q0E/s2979/10.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1041" data-original-width="2979" height="224" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhyXj0UiQEfk2WQxW9ckRApDSvNwGNqmOyzYmV1sdXMt9otCbtEFykO5mYq6HWsMFM6yr5i1C18jrCmBtWUQrhPnb1E-QamJX1a_XYiyEGUYz6xyWU-ymYMLHzEja7QXj0PnYepIUdMwmwUxTIr7gpJliCATU8Q1XmcrUHGjVrqd2bciTLImiZTSrM0Q0E/w640-h224/10.jpg" width="640" /></a></div><br /><div><br /></div><div>Now we need to put some stress on it to see how it goes.</div><div><br /></div><div><b>Step8: </b>Create a dummy project and add a test yaml as shown below.</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJyH9xy1gDaChX-LkjE3LgUh-ZMeYXfarOu7piMcHRmq2je_65Gj6j-aUWaX2iAMuDBzp7ahoFTUaE57JQk7qrMlAshkzxYxKKxKyZQlxdpp6ihARTQ2f7rRkqJMPXuOvbOziXVnQbw3EMw9NnK_oVezqkL1NzrHMqx1HSRXhbip5IvtGBPJXqKkXEiLQ/s1717/11.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="790" data-original-width="1717" height="294" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJyH9xy1gDaChX-LkjE3LgUh-ZMeYXfarOu7piMcHRmq2je_65Gj6j-aUWaX2iAMuDBzp7ahoFTUaE57JQk7qrMlAshkzxYxKKxKyZQlxdpp6ihARTQ2f7rRkqJMPXuOvbOziXVnQbw3EMw9NnK_oVezqkL1NzrHMqx1HSRXhbip5IvtGBPJXqKkXEiLQ/w640-h294/11.jpg" width="640" /></a></div><br /><div>Just create duplicate Jobs with different job names. </div><div><br /></div><div><div style="background-color: #fffffe; font-family: Consolas, "Courier New", monospace; font-size: 12px; line-height: 16px; white-space: pre;"><div><div style="line-height: 16px;"><div><span style="color: teal;">trigger</span>: <span style="color: #0451a5;">none</span></div><br /><div><span style="color: teal;">pool</span>:</div><div> <span style="color: teal;">name</span>: <span style="color: #0451a5;">'self-hosted'</span></div><br /><div><span style="color: teal;">jobs</span>:</div><div>- <span style="color: teal;">job</span>: <span style="color: #0451a5;">Job1</span></div><div> <span style="color: teal;">steps</span>:</div><div> - <span style="color: teal;">script</span>: |</div><div><span style="color: #0451a5;"> echo "Hello from $HOSTNAME [$(date) ]"</span></div><div><span style="color: #0451a5;"> echo "Sleeping for 1 min [$(date) ]"</span></div><div><span style="color: #0451a5;"> sleep 60</span></div><div><span style="color: #0451a5;"> echo "Done with sleep [$(date) ]"</span></div><br /><div>- <span style="color: teal;">job</span>: <span style="color: #0451a5;">Job2</span></div><div> <span style="color: teal;">steps</span>:</div><div> - <span style="color: teal;">script</span>: |</div><div><span style="color: #0451a5;"> echo "Hello from $HOSTNAME [$(date) ]"</span></div><div><span style="color: #0451a5;"> echo "Sleeping for 1 min [$(date) ]"</span></div><div><span style="color: #0451a5;"> sleep 60</span></div><div><span style="color: #0451a5;"> echo "Done with sleep [$(date) ]"</span></div></div></div><div><span style="color: #0451a5;"><br /></span></div><div><br /></div></div></div><div>Now if you look carefully at the pieline, the pool: parameter usually have a image:ubuntu-latest. In this case we want to run this pipeline on the self-hosted agent pool we created earlier. so you need to chnage as shown above. Run this pipeline to see our self-hosted agent pool in action.</div><div><br /></div><div><b>Step 9: </b>I created the test pipeline with 9 jobs which print what self-hosting agent it is running from and wait for a min and then print a message done sleeping with time stamps. </div><div><br /></div><div>I started the dummy pieline.</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg5oYTTAPuZPsst3qBaP2WXLxW9uq1UJGUCBzAvxRJIRvmLBvrKl5fSJA4B1QgGF68FWuoNAGs_rUDXyXx7fGkUkbiiASGUN8H9EWkJm6n2RpZ52xV8KLATkT5ClM_vPsVoy2TzG2U-QM169MOtj1ZUWi36Zqjpeq1HlM08bTwPnWgkbrpjpGbBsa3ix9o/s2923/13.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1167" data-original-width="2923" height="256" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg5oYTTAPuZPsst3qBaP2WXLxW9uq1UJGUCBzAvxRJIRvmLBvrKl5fSJA4B1QgGF68FWuoNAGs_rUDXyXx7fGkUkbiiASGUN8H9EWkJm6n2RpZ52xV8KLATkT5ClM_vPsVoy2TzG2U-QM169MOtj1ZUWi36Zqjpeq1HlM08bTwPnWgkbrpjpGbBsa3ix9o/w640-h256/13.jpg" width="640" /></a></div><br /><div>Now if you look at the VMSS instances , it will change from </div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiPidY0JSyZ7HHWZAL2nDb80Cm-yHlPc15gvm6OIKt_zs7pqXbMuJRsWb_syKo_1y2C19X7-HKHjHnM3CoTSoL0cgDkk5qFpWvX9R_ruFQZSC6FW6v77r7gi8WAjV7uHR0KbyDPBSsDJyf_Enw4BafVQzkc6GW55GSuD6NDfL1QakUcRCJzvPZK1vOvTgM/s1876/9.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="555" data-original-width="1876" height="190" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiPidY0JSyZ7HHWZAL2nDb80Cm-yHlPc15gvm6OIKt_zs7pqXbMuJRsWb_syKo_1y2C19X7-HKHjHnM3CoTSoL0cgDkk5qFpWvX9R_ruFQZSC6FW6v77r7gi8WAjV7uHR0KbyDPBSsDJyf_Enw4BafVQzkc6GW55GSuD6NDfL1QakUcRCJzvPZK1vOvTgM/w640-h190/9.jpg" width="640" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div>to this<br /><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhWyMsoYY6_3R_-xBA6vkNY1xyjQPgJChBQTKAi34p-pPWSyBrNFqHp83aLBRAdkdPCBYqYHNkFgDkxiEKEiqiSACiuTeblj3uoX0110UDKX-AEpGZuoS-IG4R1prjGRdch_b1t16L0yh3gO_w8UCr54v5wnE7FA2CLud-s-nIsRR2R_nIQsYuP3v80HXw/s2680/14.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="591" data-original-width="2680" height="142" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhWyMsoYY6_3R_-xBA6vkNY1xyjQPgJChBQTKAi34p-pPWSyBrNFqHp83aLBRAdkdPCBYqYHNkFgDkxiEKEiqiSACiuTeblj3uoX0110UDKX-AEpGZuoS-IG4R1prjGRdch_b1t16L0yh3gO_w8UCr54v5wnE7FA2CLud-s-nIsRR2R_nIQsYuP3v80HXw/w640-h142/14.jpg" width="640" /></a></div><br /><div>as soon as the instances were sucessfully provisioned, the number of jobs stated paralelly will also increase based on dependecies.</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjeGe11Ns8k9IVSeS6edus0XA4oQlxhpaiSAfZG94yFrL8mvh-tPZ15_uaNCdE9QXa_X5gExX1Z9UjnME0Sw6jOzkYZL1WkikSkeowaWV1wgUtbcu6Kcqa687q1-TxLUQY0GwNFZnspr7bN07b_BXhjwvAhgeNmtbo-azcq3G-ETAiWH8V2FqvZQVxNP4Y/s2884/15.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="516" data-original-width="2884" height="114" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjeGe11Ns8k9IVSeS6edus0XA4oQlxhpaiSAfZG94yFrL8mvh-tPZ15_uaNCdE9QXa_X5gExX1Z9UjnME0Sw6jOzkYZL1WkikSkeowaWV1wgUtbcu6Kcqa687q1-TxLUQY0GwNFZnspr7bN07b_BXhjwvAhgeNmtbo-azcq3G-ETAiWH8V2FqvZQVxNP4Y/w640-h114/15.jpg" width="640" /></a></div><br /><div><br /></div><div>and you can also se the number of agents created in our self-hosted agent pool.</div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjQkWskhOgaPTvjeimGUr-4bNsSfF2iRhIoLeb4dS3r-Nz5-eS3qhqeKg9n0VYzTYMzHIAYgn2LYNOueFtEH7vIEbAq0ye6bP6OEjEKgIkMObidzPq98wWAPfNERcrPGGeAbIyY_bQvTJ0u_PxlOkJECB8FC0lS3cY-QkqOSu64-gDg9UuCaK1ZQCiH8no/s2385/16.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="699" data-original-width="2385" height="188" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjQkWskhOgaPTvjeimGUr-4bNsSfF2iRhIoLeb4dS3r-Nz5-eS3qhqeKg9n0VYzTYMzHIAYgn2LYNOueFtEH7vIEbAq0ye6bP6OEjEKgIkMObidzPq98wWAPfNERcrPGGeAbIyY_bQvTJ0u_PxlOkJECB8FC0lS3cY-QkqOSu64-gDg9UuCaK1ZQCiH8no/w640-h188/16.jpg" width="640" /></a></div><br /> </div><div><b>Step 10:</b> Now lets say you have decided to update the artifacts that will be installed on gold image. For this you have updated the packer file and now all you need to do is, run this pipeline by selecting "update vmss" option.</div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi-1ADlwUD6uWAe7WX8WL3P9fUP1MlVMPoOYsAkWL-yJ3MPlpx366bhep3xDuqiP_JBuYFWbs44SDCa6bAWOPz75DdcJeq7Semi7goDqiu3AH7llVfEeZjFjgEFoO1RWBCIt4JIKojUAPh6tfATdHldSeeyba8n0-uZ5H5IFQdn7J7azx7PjuIKs2UcgSA/s1663/7.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="862" data-original-width="1663" height="332" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi-1ADlwUD6uWAe7WX8WL3P9fUP1MlVMPoOYsAkWL-yJ3MPlpx366bhep3xDuqiP_JBuYFWbs44SDCa6bAWOPz75DdcJeq7Semi7goDqiu3AH7llVfEeZjFjgEFoO1RWBCIt4JIKojUAPh6tfATdHldSeeyba8n0-uZ5H5IFQdn7J7azx7PjuIKs2UcgSA/w640-h332/7.jpg" width="640" /></a></div><br /> If you look into the yaml file , you can see this repo is fecting the packer file and image dependecies from <span style="background-color: #1f1f1f; color: #ce9178; font-family: Consolas, "Courier New", monospace; font-size: 14px; white-space: pre;">https://github.com/actions/runner-images.git</span></div><div>This is the MicroSoft git for runner images. But this is full set and will take 1.5 hours to build the VM image. Instead i made a packer file with subset of installer artifacts and hosted it at <span style="background-color: #1f1f1f; color: #ce9178; font-family: Consolas, "Courier New", monospace; font-size: 14px; white-space: pre;">https://github.com/pratappilaka24/custom-image.git</span></div><div>This will only take 35 minutes to build VM and 45 mints to finish end to end deployment of VMSS.</div><div><br /></div><div>Hope this helps and do let me know in case of any help needed.</div></div>Pratap Reddy Pilakahttp://www.blogger.com/profile/17141976956878163883noreply@blogger.com0tag:blogger.com,1999:blog-4208378482021219922.post-76867128534991121572023-07-07T22:30:00.011-07:002023-07-19T13:43:35.358-07:00Terraform Azure Application Landing Zone - TF AZ Bootstrap<p><b>Objective:</b> This post is to provide a kick strat your Azure DevOps journey by providing a Seed Repo for your Azure DevOps organization. Every time when a new application is about to be launched into Azure, you have to go through the provisioning of launchpad and Devops Repo and building the CI/CD pipelines. Below project will address all of those concerns. </p><p>Now i want to create similar thing and add couple of more steps and make it avilable for everyone.</p><p>Here is what you gona get.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj3hWerQ3I4o57VQl2llNFCNDqTYBZyemidUcbS3_8i3KWoZwhxxjHCwbUd6Xr1J1fKHxviC4JkMGmiMjDMyXmviUC_L5mvlJvvJSxwzC4RFrAq0JhEPPZ24T2XhIPgSPOh8ghigoUvbBO4vBtMvuF9sSVwwx0sM9Kjn4zm1V-ffxjza9ptb2aAk-uZP3U/s3840/1.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="2068" data-original-width="3840" height="344" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj3hWerQ3I4o57VQl2llNFCNDqTYBZyemidUcbS3_8i3KWoZwhxxjHCwbUd6Xr1J1fKHxviC4JkMGmiMjDMyXmviUC_L5mvlJvvJSxwzC4RFrAq0JhEPPZ24T2XhIPgSPOh8ghigoUvbBO4vBtMvuF9sSVwwx0sM9Kjn4zm1V-ffxjza9ptb2aAk-uZP3U/w640-h344/1.jpg" width="640" /></a></div><br /><a name='more'></a><p><b>Step 1:</b> Download the repo from my github repos. <a href="https://github.com/pratappilaka24/Terraform-Azure-Bootstrap" target="_blank" style="color:red">TF AZ Bootstrap</a></p><p><b>Step 2:</b> Run the AZCLI script and only 2 parameters you need to update are Subscription ID and App anme. Ask your adminstrator or customer to run it in Azure CLI / Bash.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgIrOfsV8q2LhEFYxN2wo9ETykNMw1WoVXi-ydJaPTQgrOQWmiKg4ItHd9gvo9TaTwIgWnUospRy0taws13jtQC7l_ezCyxlrXsSRwtf3UCWs3t2lL7eIDV0Jz18prl0xcrKQkh5yifj-eQaYkzYx5gAi42jPA33Lr3B27aosVt_GinZgcKJxXn50pAqGY/s1731/2.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="813" data-original-width="1731" height="300" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgIrOfsV8q2LhEFYxN2wo9ETykNMw1WoVXi-ydJaPTQgrOQWmiKg4ItHd9gvo9TaTwIgWnUospRy0taws13jtQC7l_ezCyxlrXsSRwtf3UCWs3t2lL7eIDV0Jz18prl0xcrKQkh5yifj-eQaYkzYx5gAi42jPA33Lr3B27aosVt_GinZgcKJxXn50pAqGY/w640-h300/2.jpg" width="640" /></a></div><br /><p><b>Step 3:</b> Now with the script ran, you can see a resource group created with the app name in it and you can see a Keyvault in it. Here is how all the information required for us in piplines is saved in Key Vault.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEim7fieINR_tcw-OzwT0IVsT3A1RaTMLo1y-Y4Nv_u6YE6JQAKpiNsRXfvJAzO_gPr2Xi2BrqZVV79IQoQ2VavWoP2pSofEE7Nokm6S76djXkoKfva1HqfMI_vFapU0V0UryfkfyEXEG5Qac9GZKPMhnF8wVPRG9eK_D66XvfjQvt9ju6sOrOY5zBifyxM/s1678/3.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="787" data-original-width="1678" height="300" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEim7fieINR_tcw-OzwT0IVsT3A1RaTMLo1y-Y4Nv_u6YE6JQAKpiNsRXfvJAzO_gPr2Xi2BrqZVV79IQoQ2VavWoP2pSofEE7Nokm6S76djXkoKfva1HqfMI_vFapU0V0UryfkfyEXEG5Qac9GZKPMhnF8wVPRG9eK_D66XvfjQvt9ju6sOrOY5zBifyxM/w640-h300/3.jpg" width="640" /></a></div><br /><p><b>Step 4</b>: Create a new project in Azure Devops and import the code directly from GitHub link.</p><p>Now go to pipelines, and go to library. Add a new Variable group with name "kv_variable_group" and enable Keyvault integration for Variables.</p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgUkuVGhcJ6VoZy8vVILtRF7ZRh7JxCQMNQk_Rz26inxCwM9GK7GNVYJY2fHBrXMQJnqzc3X7v4KWkHPjZaEtFXSTHMptU7rv1OHGHtECSI4IHOrpwall_7C4OtnJT6gve0DNLBYcFTbrzknRcur6FcKx96DlCeP0KdptKTazXP_B7tSUQEdd1FAWKL-VQ/s2233/4.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1851" data-original-width="2233" height="530" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgUkuVGhcJ6VoZy8vVILtRF7ZRh7JxCQMNQk_Rz26inxCwM9GK7GNVYJY2fHBrXMQJnqzc3X7v4KWkHPjZaEtFXSTHMptU7rv1OHGHtECSI4IHOrpwall_7C4OtnJT6gve0DNLBYcFTbrzknRcur6FcKx96DlCeP0KdptKTazXP_B7tSUQEdd1FAWKL-VQ/w640-h530/4.jpg" width="640" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><br /></div>Now all the infomation generated by CLI script ia avilable for pipelines in form of variables and the exact variables names have been configured in the code.<p></p><p><b>Step 5:</b> Go to pipelines, create a new pipeline from Git as source and pick existing YAML file for pipeline. Choose <b>./aure-pipelines/ci.yaml </b>and name it CI.</p><p>Give it a go:</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiNX3Jm172BaQrHWzfZI7BpH_E9_l0ek7vFQuV0uZ97E-ZOe_QjAVO_BjC8l6_Jx4mBVguh-UWjTJxeoN1qIOWicegJJg9E-JF_e35c_ZAq_sIS1UTAP8BzVH8q2S33Qm3wtJnEfX2Ci-XedeGHhd4_fg5P7URzd8qglF0U9KEOOjp4cW8fAmmDTbHgsjo/s2073/5.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="963" data-original-width="2073" height="298" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiNX3Jm172BaQrHWzfZI7BpH_E9_l0ek7vFQuV0uZ97E-ZOe_QjAVO_BjC8l6_Jx4mBVguh-UWjTJxeoN1qIOWicegJJg9E-JF_e35c_ZAq_sIS1UTAP8BzVH8q2S33Qm3wtJnEfX2Ci-XedeGHhd4_fg5P7URzd8qglF0U9KEOOjp4cW8fAmmDTbHgsjo/w640-h298/5.jpg" width="640" /></a></div><br /><p><b>Step 6:</b> Now do the same thing for <b>./azure-pipelines/cd.yaml</b> and giev it a go.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhbQqgC6ceCDJWF1egt63hU3j9SNEHwChi9P0Le9wKs2drIQowQDnoTwoyzWhGj6ThQOruxe7uFObdIEWLGQv5q8EGRdL6lorIcRUUusTSMfMcWMiEeYrnpWuaYJN4j8sXAo5vHsSsbfVPB95J6QbKkJjdkeebqg4AVg32Z2nAxf-woh72N6ksSruoNScc/s2157/6.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1146" data-original-width="2157" height="340" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhbQqgC6ceCDJWF1egt63hU3j9SNEHwChi9P0Le9wKs2drIQowQDnoTwoyzWhGj6ThQOruxe7uFObdIEWLGQv5q8EGRdL6lorIcRUUusTSMfMcWMiEeYrnpWuaYJN4j8sXAo5vHsSsbfVPB95J6QbKkJjdkeebqg4AVg32Z2nAxf-woh72N6ksSruoNScc/w640-h340/6.jpg" width="640" /></a></div><br /><p>If you run into any troubles, do let me know. I can help. </p><p> - Your fellow developer.</p>Pratap Reddy Pilakahttp://www.blogger.com/profile/17141976956878163883noreply@blogger.com0tag:blogger.com,1999:blog-4208378482021219922.post-89863964050789914702023-06-11T00:51:00.003-07:002023-06-11T14:28:12.283-07:00Deploying Virtual WAN using Terraform & Azure DevOps <p><b>Let me summarize Azure networking options based on usecase:</b></p><p></p><ul style="text-align: left;"><li>You need network connectivity between resources across different virtual networks in same region, you need to implement VNet peering.</li><li>You need connectivity between resources in virtual networks spanned across different region, you need to implement Global VNet peering.</li><li>You need network connectivity between your Organization (On-Prem) and your azure tenant and you are ok to have the secure channels over the internet, You need to implement site to site VPN gateways.</li><li>You want network connectivity between your offices to azure tenancy with high throughput and not over internet, you need to implement Express Route.</li><li>You need individual users to use services hsoted in your Azure tenant, you will implemnet Point-to-site VPN gateway.</li></ul><p></p><p> All the above implementations are different on thier configurations and they each cater for each use case in its own capacity.</p><p><b>Here is why you need to choose Virtual WAN if you are already using more than 2 capabilities mentioned above.</b></p><p></p><ul style="text-align: left;"><li> VWAN brings all of the above network connectivity implemntations under one centralized platform.</li><li>VWAN automatically deployes one hub in each choosen region which implements Hub-spoke network design by default.</li><li>Site-to-Site VPN gateways supports max of 10, 30 and 100 tunnels in Basic, Standard and HighPerformance SKUs. VWAN supports upto 1000 branch conncetions per VWAN hub, which can throuhput at 20GBps per hub.</li><li>Though private communication between VNets in both VNet Peering and VWAN are ecrypted over MS backbone network, Adding additional firewall security is way easier in VWAN comapred to VNet peering.</li><li>VWAN has most of the above services deployed across all avilability zones in a given region thus making it more relaible and scalable without any manual intervention.</li><li>Virtual WAN provides many functionalities built into a single pane of glass such as site/site-to-site VPN connectivity, User/P2S connectivity, ExpressRoute connectivity, virtual network connectivity, VPN ExpressRoute Interconnectivity, VNet-to-VNet transitive connectivity, Centralized Routing, Azure Firewall and Firewall Manager security, Monitoring, ExpressRoute Encryption, and many other capabilities. Pick and choose what you want.</li></ul><p></p><p>More information is available on MS Documentation. All refrence links are provided at the end of the article.</p><p>Now the title of artice is no "<b>Why VWAN?</b>" it says "<b>Deploying VWAN using Terrafrom & Azure Devops</b>". So lets jump in to deployment.</p><a name='more'></a><br /><p>My objective is to deploy Virtual WAN, VWAN Hubs across 2 different regions, connect 2 Virtual networks in these regions via VWAN Hubs, deploy 2 Linux VMs in these VNets, have a ping from one VM to other one over VNet connectivity of VWAN and Hubs.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiRPy-GRMmyo7Q-_nC6kMJ_g2E_U9ScJIkjZbNkGxzGjexWaSSc-rwkyCYyNMTiF0Qvj8rIWhlV4pUzOQzZfbsmq8WJ_zdio85ZT2OsaFhoG0GMs5V_LuIoL3pmPOUqvkamOsrfL2mYaQIdwQcMMi8bgLuZYeIa9xJn0F6mZyVtEW_e3TTEDiL8XHMN/s768/vWAN-DemoLab-Overview.webp" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="612" data-original-width="768" height="510" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiRPy-GRMmyo7Q-_nC6kMJ_g2E_U9ScJIkjZbNkGxzGjexWaSSc-rwkyCYyNMTiF0Qvj8rIWhlV4pUzOQzZfbsmq8WJ_zdio85ZT2OsaFhoG0GMs5V_LuIoL3pmPOUqvkamOsrfL2mYaQIdwQcMMi8bgLuZYeIa9xJn0F6mZyVtEW_e3TTEDiL8XHMN/w640-h510/vWAN-DemoLab-Overview.webp" width="640" /></a></div>I have burned heavily while implemnting this , once by deploying Preminum SKU for firewalls, other times deploying the bation services for these VMs , which i dont need for purpose of my POC.<div><br /><div>But if you are interested the code is there in terraform files commented. You can uncomment before you deploy. </div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEilMghO5UN7de5LRv2qQzxkGkQwx9CRy5X_GJRlWEvZewk-JiVkxOEpgwrm9Xqqfeujqyc6fbO2JeMY8I34dmhHFMgl8zQIf1fEPDPgQGonP1U8zO-uJEGUIPV1pemM1oPyMZPMO1azTXR_cH4nlHKoUMWh54eVYXgWMYQ5T9B7aniR_6A50qmNpZxh/s1804/1.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="919" data-original-width="1804" height="326" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEilMghO5UN7de5LRv2qQzxkGkQwx9CRy5X_GJRlWEvZewk-JiVkxOEpgwrm9Xqqfeujqyc6fbO2JeMY8I34dmhHFMgl8zQIf1fEPDPgQGonP1U8zO-uJEGUIPV1pemM1oPyMZPMO1azTXR_cH4nlHKoUMWh54eVYXgWMYQ5T9B7aniR_6A50qmNpZxh/w640-h326/1.png" width="640" /></a></div><br /><div>So the code i will be deploying no bation services for the VMs and used the standard version of VWAN hub firewall. <br /><p><b>Step1</b>: Create a Storage account with continer to manage your terrafrom state file.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiiQyWvpa-UDyEG_VyyD2FQa5jlrYTtqtD-XtCjfwYi1rJSZvwy9GHCtzDdKT04EW0Sv2i8sE3Z19UcIyo_UlTpAUktcdcO2eganlY4CPSI4I15xRoYxMxdCEavbQ8NCTB_7rlolvoHTymDKCNi_heG9u3g-g0VpnkUAmFmYL8mEn_ianOHcYOo0LIW/s1777/2.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="747" data-original-width="1777" height="270" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiiQyWvpa-UDyEG_VyyD2FQa5jlrYTtqtD-XtCjfwYi1rJSZvwy9GHCtzDdKT04EW0Sv2i8sE3Z19UcIyo_UlTpAUktcdcO2eganlY4CPSI4I15xRoYxMxdCEavbQ8NCTB_7rlolvoHTymDKCNi_heG9u3g-g0VpnkUAmFmYL8mEn_ianOHcYOo0LIW/w640-h270/2.png" width="640" /></a></div><br /><p><b>Step2</b>: Download the code from my Git repo.</p><p><a href="https://github.com/pratappilaka24/Azure-VWAN">https://github.com/pratappilaka24/Azure-VWAN</a></p><p>Azure DevOps is optional, as you can literally run terrafrom apply. But this needs installing terraform on your machine and AZ CLI or Azure shell for connectiing to your azure tenant. </p><p><b>Step 3</b>: Create a Project in upload the code to your AZ repo.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhlY9YGmrXVVaFnZNWQ0VfAO5im6gYN1lQRLxZs2TZeXP5nBB1c8_b4S1olKUwHsGLt7NhX_fZ91OuU4FL0WdI4VAOBIq1hhaMDOA_J3iYUIDHZcItZIXmKdIByAR0-Zmp8jvwtk2fiapmv0mWLD2N_OEKaoItYTDen9yoHjhdVB6bAFV7b1iMvzHzA/s2173/3.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1096" data-original-width="2173" height="322" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhlY9YGmrXVVaFnZNWQ0VfAO5im6gYN1lQRLxZs2TZeXP5nBB1c8_b4S1olKUwHsGLt7NhX_fZ91OuU4FL0WdI4VAOBIq1hhaMDOA_J3iYUIDHZcItZIXmKdIByAR0-Zmp8jvwtk2fiapmv0mWLD2N_OEKaoItYTDen9yoHjhdVB6bAFV7b1iMvzHzA/w640-h322/3.png" width="640" /></a></div><br /><p><b>Step 4</b>: Create a 2 release pipelines , one for creating , the other for destroying. </p><p>Add Artifacts to depoy by adding the current project as working folder.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjAePIjj17iff4GHplyUcH1BakL63_RCjN_f1l6aHR4j8vZetJz9OQUAirNm9dv0St72KT3GOutP2zP3vRaLhN8boQRpOHI42H4JWSHPjN32aX0Qeb_6JObjTKIHw8rUkGJSRz5PTdSk1qhrc6VEkEFmoslC8OLAZ1d-F44hOU5sGXlZpps-2CIGtgI/s2235/5.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1237" data-original-width="2235" height="354" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjAePIjj17iff4GHplyUcH1BakL63_RCjN_f1l6aHR4j8vZetJz9OQUAirNm9dv0St72KT3GOutP2zP3vRaLhN8boQRpOHI42H4JWSHPjN32aX0Qeb_6JObjTKIHw8rUkGJSRz5PTdSk1qhrc6VEkEFmoslC8OLAZ1d-F44hOU5sGXlZpps-2CIGtgI/w640-h354/5.png" width="640" /></a></div><br /><p><b>Step5</b>: Start editing the stage1 by adding the below shown steps.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgovNpdmZuOgXgmj7gamLKHed4fjJCQ2E8o1KYMPuc-alf2nX_fZZlWxusvxTl9dUbOE-nh7qIRqYlmmc-VA8tij0ZvGZ7sMsdvFbVY71ASLZkNMEPNK-wss7FToOeql_ebhpA6nwqEepSaPZWxRWxYlCsL6-KRY9Uhdsduk1u1rhRRI4wUUMWaYDY7/s1282/4.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="939" data-original-width="1282" height="468" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgovNpdmZuOgXgmj7gamLKHed4fjJCQ2E8o1KYMPuc-alf2nX_fZZlWxusvxTl9dUbOE-nh7qIRqYlmmc-VA8tij0ZvGZ7sMsdvFbVY71ASLZkNMEPNK-wss7FToOeql_ebhpA6nwqEepSaPZWxRWxYlCsL6-KRY9Uhdsduk1u1rhRRI4wUUMWaYDY7/w640-h468/4.png" width="640" /></a></div><br /><p>The only catch you might have here is that you need to point .tfvars file. It can be done by passing the name of tfvars file as a command line argument for apply and destroy steps.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjgC_hkalzrPIccOOwIbPdCZyfs-yDMg6D-G4ZtAHlhC1cpSWpXGYfT8x7RAdHefXOQ6NLuT4L81smeJOekkcLjWbwARIAf8OOSSAU1UhRw3lydWXA0uL-oWV6Zh5joWb09bJtIYRw6N7Q4Um2briP6DvMqlMi_7o-2iWbNKSDY3ksvY4qjgTgVlcb6/s964/6.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="819" data-original-width="964" height="544" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjgC_hkalzrPIccOOwIbPdCZyfs-yDMg6D-G4ZtAHlhC1cpSWpXGYfT8x7RAdHefXOQ6NLuT4L81smeJOekkcLjWbwARIAf8OOSSAU1UhRw3lydWXA0uL-oWV6Zh5joWb09bJtIYRw6N7Q4Um2briP6DvMqlMi_7o-2iWbNKSDY3ksvY4qjgTgVlcb6/w640-h544/6.png" width="640" /></a></div><br /><p><b>Step 6:</b> Deploy the apply release pipeline and you will see two resource groups one in AUE and USE regions. I have deployed below components as part of this project:</p><p></p><ul style="text-align: left;"><li>2 Resource groups in different regions</li><li>2 vnets</li><li>2 Linux VMs and supporting NSGs, NICs and OS Disks</li><li>1 Virtual WAN</li><li>2 VWAN Hubs one for each region</li><li>2 firewall policies and firewalls securing 2 HubsVNets</li><li>1 Keyvault saving the random password for the Linux VMs</li></ul><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiQh7kfZxpKpBWDupVAORXn_Pgpjr-Wl3LjnoPiRUva_bHyHhSIo0zPLhN0T-hDxkVOWqwd25RYB5rfkx7b20rXt7CHOrmvBRd7KDinbp0VxvajOpiyHvIT_kSu35SQmOUsQ10uBXBX7VSs6j59BJE1Uqr9fe62AhkRBNSDRMFAXQ0B1eYohf50nX8Z/s3612/7.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1750" data-original-width="3612" height="310" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiQh7kfZxpKpBWDupVAORXn_Pgpjr-Wl3LjnoPiRUva_bHyHhSIo0zPLhN0T-hDxkVOWqwd25RYB5rfkx7b20rXt7CHOrmvBRd7KDinbp0VxvajOpiyHvIT_kSu35SQmOUsQ10uBXBX7VSs6j59BJE1Uqr9fe62AhkRBNSDRMFAXQ0B1eYohf50nX8Z/w640-h310/7.png" width="640" /></a></div><br /><p>Please note that the Linux VMs has SSH opened , use pasword login and have PIPs assigned to them, for easy of testing. In production envirment , aslways go for bastion and no open SSH.</p><p>Even though we open SSH, the login password (random generated pasword) will be saved to a Keyvault created in AUE resource group.</p><p><b>Step7:</b> Before we login to VMs, lets check VWAN. You will find it in AUE region as shown above. Open VWAN and you can see the Hubs deployed and supporting firewalls to secure the hubs.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEggWITPpq6MET9UavoKxnspA8CtCKdeDw0K-t8ixfHzlZ-dH0BbEWYIOroXUB2GG7_Bae1PnWgUHxKgsNMBCfUNIHI9YaSPxloPxOZLeYFrU1nyE9M_SuxpmE04IVJGDmxmGii4OnamI7yaFi5rIriC5Ll-3skVAO2L5pAwOGrFLoNAsEoUgBh-WlS9/s1894/8.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="883" data-original-width="1894" height="298" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEggWITPpq6MET9UavoKxnspA8CtCKdeDw0K-t8ixfHzlZ-dH0BbEWYIOroXUB2GG7_Bae1PnWgUHxKgsNMBCfUNIHI9YaSPxloPxOZLeYFrU1nyE9M_SuxpmE04IVJGDmxmGii4OnamI7yaFi5rIriC5Ll-3skVAO2L5pAwOGrFLoNAsEoUgBh-WlS9/w640-h298/8.png" width="640" /></a></div><br /><p>Click on the View Topology link in overview to see the whole hub-spoke architecture you just deployed.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhdV_JszkcAVPuX2qR46vjwrsNzJt-xXUNQk_PT54g7GF4r1T_rSWlLDGR7KgulsLPvom7wp6eDRtWer3sm-OeffijxxuAVEZOVX47RlZ_NFe8UPlzFtwDqYraCogtgUNUNIdahPfrbxtU2Q75BEk5IR2-VbjneVJa62WFsBHp_DU2lGtoLkoXnraUS/s1887/9.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1447" data-original-width="1887" height="490" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhdV_JszkcAVPuX2qR46vjwrsNzJt-xXUNQk_PT54g7GF4r1T_rSWlLDGR7KgulsLPvom7wp6eDRtWer3sm-OeffijxxuAVEZOVX47RlZ_NFe8UPlzFtwDqYraCogtgUNUNIdahPfrbxtU2Q75BEk5IR2-VbjneVJa62WFsBHp_DU2lGtoLkoXnraUS/w640-h490/9.png" width="640" /></a></div><br /><p><b>Step 8: </b>Open Firewall-policy and you can see that these will allow the private communication from IP range of one VNets to other one. </p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj1Xz9WfqKO4QR0sY6oqzuXVQyvsC9RhzS0Q_VqkBjQ9-sQO5i7doRFsqcuItq0SzlkzA90-83Br6x28dE5YD2wtwdDb4u3GiMlpokob0UCOmF8Glr77ITW-xsu4epB9Cd7mKjRn_TFUF_8XIunNUzRsC0jmvNXQC7IUSUhTnuNZoT4oiQJzUaIU6g4/s2206/10.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="721" data-original-width="2206" height="210" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj1Xz9WfqKO4QR0sY6oqzuXVQyvsC9RhzS0Q_VqkBjQ9-sQO5i7doRFsqcuItq0SzlkzA90-83Br6x28dE5YD2wtwdDb4u3GiMlpokob0UCOmF8Glr77ITW-xsu4epB9Cd7mKjRn_TFUF_8XIunNUzRsC0jmvNXQC7IUSUhTnuNZoT4oiQJzUaIU6g4/w640-h210/10.png" width="640" /></a></div><br /><p>This means i can define a common set of firewall policies and apply the firewall security to communication over hubs. Now you can centrally control the policies applied to all comms across your network.</p><p><b>Step 9:</b> Lastly open one of the Hubs and click "Routing Intent and Routing Policies", here you can control policies applied for the traffic over the internet and the private traffic that runs on MS backbone.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjjg03npP1NJhJAY8PbI_2kqdAsWYqdLbqeI86ggFBpxWwIq8e3-diAFfgt7sxzouY_V4s-7qrarDmFv7cYjw59Ryn3rawxL2PTaCJy7E35ErUDqO61TjNG-GY9F7nrM41JvbHYUJ-pWMyYhbKSC8d9dx8QbxNOf6nZBcbG6kao-WwLjVbuaw-obQkK/s1999/11.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="775" data-original-width="1999" height="248" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjjg03npP1NJhJAY8PbI_2kqdAsWYqdLbqeI86ggFBpxWwIq8e3-diAFfgt7sxzouY_V4s-7qrarDmFv7cYjw59Ryn3rawxL2PTaCJy7E35ErUDqO61TjNG-GY9F7nrM41JvbHYUJ-pWMyYhbKSC8d9dx8QbxNOf6nZBcbG6kao-WwLjVbuaw-obQkK/w640-h248/11.png" width="640" /></a></div><br /><p><b>Step 10: </b>Finally go to Keyvault and grab the VM password. </p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi8k7ivR7xptMlGxTEKucHkBsPYFt-Hlvm9rbiXaeMWanHnMrRc2FIc0kLQO7_sHyZ1CSZjnzYGCq3PRjpNc1g_RZT53qlUEq79N5eJy5BpUC7K1zLq9fRQsdIbi8j9000Nl_GklhFeVlY9Nr8HdQ02GmRAyyglXvkd1rCzJZOfAegkdwdU4Zd7qL77/s1525/12.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="760" data-original-width="1525" height="318" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi8k7ivR7xptMlGxTEKucHkBsPYFt-Hlvm9rbiXaeMWanHnMrRc2FIc0kLQO7_sHyZ1CSZjnzYGCq3PRjpNc1g_RZT53qlUEq79N5eJy5BpUC7K1zLq9fRQsdIbi8j9000Nl_GklhFeVlY9Nr8HdQ02GmRAyyglXvkd1rCzJZOfAegkdwdU4Zd7qL77/w640-h318/12.png" width="640" /></a></div><br /><p>Please know that i opened SSH and addd a public IP just for our testing purpose. Else you should use bastion. Code for bastion is also there in the project files. Just uncomment it.</p><p>I login to AUE VM and try pinging the private IP of USE VM.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjtK-K4LyJmdMF0bgHQcTakeyucvY_FdPoaJe2rraMcnm9PAFSHoToAnH76o_SE_0Ti5dSroQbCyZdalifbc8I0MCaITo5zLTmoEv7ZGGR2SYCQEcaw0ixr2JvdQ06B1WyX7biiIHijInMXr6WHGhfKy1tbwQ51IpMToLuYv32jq4bxOlZZ0cqpX5dS/s853/13.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="378" data-original-width="853" height="284" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjtK-K4LyJmdMF0bgHQcTakeyucvY_FdPoaJe2rraMcnm9PAFSHoToAnH76o_SE_0Ti5dSroQbCyZdalifbc8I0MCaITo5zLTmoEv7ZGGR2SYCQEcaw0ixr2JvdQ06B1WyX7biiIHijInMXr6WHGhfKy1tbwQ51IpMToLuYv32jq4bxOlZZ0cqpX5dS/w640-h284/13.png" width="640" /></a></div><br /><p>Same goes from USE VM to AUE VM.</p><p>If i ping the public IP, this wont work. Reason being, the firewall policy currently allow only the communcation between the private IP range of the VNets.</p><p>There now run the destroy pipeline after your testing to save money on your dev subscription 😃</p><p> </p><p><b>Refrences:</b></p><p>Article by Jake Walsh helped a lot to understand and formulate this project. Big thanks to him. <a href="https://jakewalsh.co.uk/deploying-azure-virtual-wan-using-terraform/">Link</a></p><p>Best place to start after exploring VWAN to know how it is advantageous over other implementations. <a href="https://learn.microsoft.com/en-us/azure/virtual-wan/virtual-wan-faq">Link</a></p><p>The mother of all <a href="https://learn.microsoft.com/en-us/azure/virtual-wan/">Link</a></p><div class="separator" style="clear: both; text-align: center;"><br /></div><br /><p><br /></p><p><br /></p><p><br /></p><p><br /></p></div></div>Pratap Reddy Pilakahttp://www.blogger.com/profile/17141976956878163883noreply@blogger.com0tag:blogger.com,1999:blog-4208378482021219922.post-42147921337920617312022-03-26T18:55:00.002-07:002022-03-27T13:12:00.754-07:00How to build subscription based security around Azure functions<p>Working in company which deals with hundreds of client azure tenants showed me how different it is working on your own tenant.</p><p>Recently i worked on a subscription based service and i want to show you how to build the secruity walls arround your azure functions.</p><p>Here is an example of subscriotion service which caters differently for each client based on thier type of subscription. Free or Paid or Premium. </p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgC_FL3gv00Q5SZ69qQxt4x_us2bt03ry7O3wvPryPYIZXyU9GzwDtAZnrcYyvqb7otnUg24EtJ3ClbuPJf8Z6-UTRF1hvW2yFpQuWwBZV90XmZErXCoZKG_NJ1QInHj5xhU0GQZ7TjwXUtyYa84Gx_rn7beSvri4kVgNMFkhLWLEd4xvhgwx3mECxc/s1005/1.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="570" data-original-width="1005" height="362" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgC_FL3gv00Q5SZ69qQxt4x_us2bt03ry7O3wvPryPYIZXyU9GzwDtAZnrcYyvqb7otnUg24EtJ3ClbuPJf8Z6-UTRF1hvW2yFpQuWwBZV90XmZErXCoZKG_NJ1QInHj5xhU0GQZ7TjwXUtyYa84Gx_rn7beSvri4kVgNMFkhLWLEd4xvhgwx3mECxc/w640-h362/1.png" width="640" /></a></div><a name='more'></a><br /><p><b>Step 1:</b> Lets start with creating a smiple azure function in Visual Studio. For the purposes of our example created two functions one with GET and one with POST methods.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgflEx0JmoLxv-GXB3d5tcZxxqOp7bpZgNmNpNnnWKmb-IqFM5h-wzPGYUtgzaUI7l21vdFch7f6iAK5MyZwuDS6AYQCAL1dAOT5w0WXp14xlDn_5ICPXr7ZENxGWg7mRzFSK0dwnQFI6l7JrlLa8lQAjxxQ6AtrjWX10ozFp3RZDarQk-T3bZy6aiD/s1377/2.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="864" data-original-width="1377" height="402" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgflEx0JmoLxv-GXB3d5tcZxxqOp7bpZgNmNpNnnWKmb-IqFM5h-wzPGYUtgzaUI7l21vdFch7f6iAK5MyZwuDS6AYQCAL1dAOT5w0WXp14xlDn_5ICPXr7ZENxGWg7mRzFSK0dwnQFI6l7JrlLa8lQAjxxQ6AtrjWX10ozFp3RZDarQk-T3bZy6aiD/w640-h402/2.png" width="640" /></a></div><br /><p>Publish this function project to Azure tenant and see it run and test.</p><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhaFMFQSdXMx9Z9Yrw3YqNdu4I0psbcjl3yDSZl3Z6b6OKDDkXjgTdBbpMWY3w4egW2faBG3sbVGkNaeR0DXKIlSUeA1m8IDYpcT7R93bAswEPcs-xuIFOpkwCAyErtACG0RCX58yzrktPoHg_bhnRUqwFZ7xc0WJUrHAfc9L9H8U09bGBL1K9BNd9j/s1472/4.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="431" data-original-width="1472" height="188" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhaFMFQSdXMx9Z9Yrw3YqNdu4I0psbcjl3yDSZl3Z6b6OKDDkXjgTdBbpMWY3w4egW2faBG3sbVGkNaeR0DXKIlSUeA1m8IDYpcT7R93bAswEPcs-xuIFOpkwCAyErtACG0RCX58yzrktPoHg_bhnRUqwFZ7xc0WJUrHAfc9L9H8U09bGBL1K9BNd9j/w640-h188/4.png" width="640" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhJpSvjogDUa63dIrwI6AhsPnthJJjqOrIo-qWQdRCAl5tvbT206rKDTj8LHN82jvQFb9mMDpBam2yN8GXFYVQ2SHX3T4hwckP1KNrnfNGIQGbVwvLNsak0lYtI1HLl-d6Ux1AX4NPp9sto6aFyXvjpYJ1i17q-5P_bH-fouWuv_JzGqNPXP6gpR5Pp/s1619/5.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="473" data-original-width="1619" height="186" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhJpSvjogDUa63dIrwI6AhsPnthJJjqOrIo-qWQdRCAl5tvbT206rKDTj8LHN82jvQFb9mMDpBam2yN8GXFYVQ2SHX3T4hwckP1KNrnfNGIQGbVwvLNsak0lYtI1HLl-d6Ux1AX4NPp9sto6aFyXvjpYJ1i17q-5P_bH-fouWuv_JzGqNPXP6gpR5Pp/w640-h186/5.png" width="640" /></a></div><div><br /></div><b><div><b><br /></b></div>Step 2: </b>Now lets implement OAuth on top of this Function App. To do this go to Function App and go to Authentication. Add an Identity Provider and pick Microsoft Idenity and make an App Registration.<div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjpdyoYIy-Jq_BP-zEkcHo-tK-EKvoXZfmkPgBl8wJDUkGx5NHBL9sk5MjLxRfYhWJo2YW7rJj7u9fdFuBpvDwsDtNLbeGio-n0zCqQxCVvVwepESW4CuDJ4VzqCWkaostKpxG0kXL2vr70Z9Ya2rW0DTXJHlUNn8sKt_0bYw_Bik1SEVtA62PCLjY2/s1311/6.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1026" data-original-width="1311" height="500" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjpdyoYIy-Jq_BP-zEkcHo-tK-EKvoXZfmkPgBl8wJDUkGx5NHBL9sk5MjLxRfYhWJo2YW7rJj7u9fdFuBpvDwsDtNLbeGio-n0zCqQxCVvVwepESW4CuDJ4VzqCWkaostKpxG0kXL2vr70Z9Ya2rW0DTXJHlUNn8sKt_0bYw_Bik1SEVtA62PCLjY2/w640-h500/6.png" width="640" /></a></div><div><br /></div>Now there is a OAuth layer and you can see that the functions wont respond with out a OAuth token.</div><div>The will respond with "<span face=""Segoe UI", "Segoe UI Web (West European)", "Segoe UI", -apple-system, BlinkMacSystemFont, Roboto, "Helvetica Neue", sans-serif" style="background-color: white; color: #333333; font-size: 14px;">401 Unauthorized</span>":"<span face=""Segoe UI", "Segoe UI Web (West European)", "Segoe UI", -apple-system, BlinkMacSystemFont, Roboto, "Helvetica Neue", sans-serif" style="background-color: rgba(128, 128, 128, 0.1); color: #333333; font-size: 14px; white-space: pre-wrap;">You do not have permission to view this directory or page.</span>"</div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjJRRXrvaxgiUQRTMCssZcdhI-ptRuB8bFow3j8L6YFbS3kluTmOJOxJ-7CXmW2NdYjgKJW0s0v2B1CbaMaSmCd2_E3nvR31XESZ6cR3IuPvYprH53oaSPf-QeS1YxT-SfX7-tEtRmXNvf1jC1cGltjytfVL-1Qhqoh5Pn21AK90XiMgZHzXawqfCYD/s1638/7.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="459" data-original-width="1638" height="180" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjJRRXrvaxgiUQRTMCssZcdhI-ptRuB8bFow3j8L6YFbS3kluTmOJOxJ-7CXmW2NdYjgKJW0s0v2B1CbaMaSmCd2_E3nvR31XESZ6cR3IuPvYprH53oaSPf-QeS1YxT-SfX7-tEtRmXNvf1jC1cGltjytfVL-1Qhqoh5Pn21AK90XiMgZHzXawqfCYD/w640-h180/7.png" width="640" /></a></div><br /><div>Now lets get that OAuth token using a postman request.</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiO58fLxIqP8cVBXq8RFG52vUSJ17VIekGBGKH2wEm_RyWDQMjsQtHGw9U2JJrthjGVzy5pwNbxlKrQis7C0RaDSha1VCnhKk2fQBN5B8zLtrTDOu9U_CARCTRmcumRPSdQmibUb_pyKLUcMJzWzqxFs0t5rFw7KfqFpyETQ32mSYU5HI49HYZEcZkI/s1679/8.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="954" data-original-width="1679" height="364" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiO58fLxIqP8cVBXq8RFG52vUSJ17VIekGBGKH2wEm_RyWDQMjsQtHGw9U2JJrthjGVzy5pwNbxlKrQis7C0RaDSha1VCnhKk2fQBN5B8zLtrTDOu9U_CARCTRmcumRPSdQmibUb_pyKLUcMJzWzqxFs0t5rFw7KfqFpyETQ32mSYU5HI49HYZEcZkI/w640-h364/8.png" width="640" /></a></div><br /><div>Now lets fire that request using the Bearer token.</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgV1iB6_7yXcB1Lp0ePGbpx8_vjg_ZVBpmpWJh6n87EQM_g7c9bDSk9I9fSJQhVwAmrz3IILAvzdx4G8yQGFmAuLPYEf04YHN5aiK4yqYK_c_L-YNn2oBtcA075uvFWLFKCMuuqI6tECg5_arDICjb3YwR3y7d2fS5jtDhsRLxdbE65HQI2XV65vKzA/s1530/10.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="475" data-original-width="1530" height="198" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgV1iB6_7yXcB1Lp0ePGbpx8_vjg_ZVBpmpWJh6n87EQM_g7c9bDSk9I9fSJQhVwAmrz3IILAvzdx4G8yQGFmAuLPYEf04YHN5aiK4yqYK_c_L-YNn2oBtcA075uvFWLFKCMuuqI6tECg5_arDICjb3YwR3y7d2fS5jtDhsRLxdbE65HQI2XV65vKzA/w640-h198/10.png" width="640" /></a></div><br /><div>Ok now with Oauth in place, we got one layer of protection. But this doesnt differentiate Free, Paid or Premium clients. They all can access the Azure functionality the same after having that OAuth token.</div><div><br /><p><b>Step 3:</b> Now its time for building the subscription based security layer around the OAuth enabled AZ functions. This is how it looks like by end of our configuration.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgX5kCb6OaXxPocqb29iEbgs-xlywvmK0yY0XzDH6RpfwtS80j1Wh6fJFnbUfgkrvRHjugTWp03BkSCLKcCH6x0wu5tYFbw2GhtOmSSamhVsmVLfNRHN1IPyeiVssPGX-HlgVTuaAfQC8i4AyEHatxpLrWkHcl-Rbzwd8ozwTSLqVTl08D88MSsbzYu/s1159/11.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="598" data-original-width="1159" height="330" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgX5kCb6OaXxPocqb29iEbgs-xlywvmK0yY0XzDH6RpfwtS80j1Wh6fJFnbUfgkrvRHjugTWp03BkSCLKcCH6x0wu5tYFbw2GhtOmSSamhVsmVLfNRHN1IPyeiVssPGX-HlgVTuaAfQC8i4AyEHatxpLrWkHcl-Rbzwd8ozwTSLqVTl08D88MSsbzYu/w640-h330/11.png" width="640" /></a></div><br /><p>Lets start with creating a Azure API Management Service. It will take more than 10 mins to provision this.</p><p>Once created go to APIs and create new one selecting FunctionApp template and choose the functions exposed over that API. I created 3 API endpoints one for Free with just GetFunction(), Paid endpoint with POSTMethod() and Premium with all Functions.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhb_DgTAN-ArJLcioYi-ORsGOQDarmFPOjVIKD0lGwXPG-DUMfqyW02zgiJ19NeSF36IZsKAewwfkO4PhkL280RFPNUyn8QMGbpf3QXZtonUkAcHen0nWdfb8_1jWK_-k-GKeDwgpQCOF1hpRCcY46KOp71BI0ImXOJPEYqa9ZYWzhuGAY_Co0OgNZ9/s1222/16.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="827" data-original-width="1222" height="434" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhb_DgTAN-ArJLcioYi-ORsGOQDarmFPOjVIKD0lGwXPG-DUMfqyW02zgiJ19NeSF36IZsKAewwfkO4PhkL280RFPNUyn8QMGbpf3QXZtonUkAcHen0nWdfb8_1jWK_-k-GKeDwgpQCOF1hpRCcY46KOp71BI0ImXOJPEYqa9ZYWzhuGAY_Co0OgNZ9/w640-h434/16.png" width="640" /></a></div><br /><p>Now lets test the Premium API with OAuth authorization. and see the result.</p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj0fqvFnqMlpFPgtGktxSKskH5rmWY5kbLMHjZkcuMuoL8Fg-bKAlat3ZYZWOi1lsD9901R2vZX-tAJNokV5qB4HwHK9e_kKcby1XOfZOXr_6rQk5JdPG2R90tWZGfwOgIjhaoe4dJVv8tIbadJ8CllRsiMlBnz_KV4M86p4It4ZQecWFnRa-u8cKoN/s1837/17.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="847" data-original-width="1837" height="296" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj0fqvFnqMlpFPgtGktxSKskH5rmWY5kbLMHjZkcuMuoL8Fg-bKAlat3ZYZWOi1lsD9901R2vZX-tAJNokV5qB4HwHK9e_kKcby1XOfZOXr_6rQk5JdPG2R90tWZGfwOgIjhaoe4dJVv8tIbadJ8CllRsiMlBnz_KV4M86p4It4ZQecWFnRa-u8cKoN/w640-h296/17.png" width="640" /></a></div><p></p><p>Result:</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiceZbr2-OIf1Qq0BEipiEKTTImh5y79oqliK63ll9in7kv0Wpg6_EmxoThdnNCFoLg9Vpkn-Meb7P2HCIitxXifn4d3uvCSYZV0Bhdi2O1k2JlLQrJogrQe0O6dmbe5rw8ETYM1VSciWtZRJ--9D71qfZkJcpkvtB3nCDtU8FwHC9P3uKm9pGOARz_/s2047/18.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1073" data-original-width="2047" height="336" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiceZbr2-OIf1Qq0BEipiEKTTImh5y79oqliK63ll9in7kv0Wpg6_EmxoThdnNCFoLg9Vpkn-Meb7P2HCIitxXifn4d3uvCSYZV0Bhdi2O1k2JlLQrJogrQe0O6dmbe5rw8ETYM1VSciWtZRJ--9D71qfZkJcpkvtB3nCDtU8FwHC9P3uKm9pGOARz_/w640-h336/18.png" width="640" /></a></div><br /><p>Step 4: Creating AZ API Subscriptions for each client with set API access is a best practice. So lets start creating the Sub Keys for each client and select the API for which they paid for.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjOTN3Yr7hbxIXx4tOxncLq1nO9czGGKx2riUdcuG0TGwYGfkdl3jWy-3FfRV_q3UkMfbXLlBq-RyF-9Z37iQPdV3jNRhQtPVE3kKH5WH8UFd4Eg-cW32jS8Smm3bhXR4XoQ-PAadffIkiZeCRyzr20X9cqnfMbbF4pE6-0orMdvfaJnobpuhot0MaJ/s541/19.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="541" data-original-width="494" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjOTN3Yr7hbxIXx4tOxncLq1nO9czGGKx2riUdcuG0TGwYGfkdl3jWy-3FfRV_q3UkMfbXLlBq-RyF-9Z37iQPdV3jNRhQtPVE3kKH5WH8UFd4Eg-cW32jS8Smm3bhXR4XoQ-PAadffIkiZeCRyzr20X9cqnfMbbF4pE6-0orMdvfaJnobpuhot0MaJ/w365-h400/19.png" width="365" /></a></div><br /><p>Each subscription will have a primary and secondary key for access request. so now we send those clients these Subscription Keys and they need both OAuth key and thier own subscription key to access the azure functions.</p><p>Now when we made a request to API with OAuth it clearly says it needs the Subscription Key to access.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh46BI0gOXu99CUad9dhpbePsjjY3TQFoOlJ6QdXju-2QlO8kIVCi_dxgCbjr8sfSdhjqry-ikzEuZLo4-hn97oub8-KAMLIyTcUWgIBejULmDBem4qT3qowHrOizcwhlY0BA35cMW_2urgPEeS2as5Bo5_9fCoXp7RTCRsGieVfoibnYaHAvLPMGQH/s1631/20.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="706" data-original-width="1631" height="278" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh46BI0gOXu99CUad9dhpbePsjjY3TQFoOlJ6QdXju-2QlO8kIVCi_dxgCbjr8sfSdhjqry-ikzEuZLo4-hn97oub8-KAMLIyTcUWgIBejULmDBem4qT3qowHrOizcwhlY0BA35cMW_2urgPEeS2as5Bo5_9fCoXp7RTCRsGieVfoibnYaHAvLPMGQH/w640-h278/20.png" width="640" /></a></div><br /><p>Now lets try with both with OAuth tokens and Subscription key, it goes thorugh sucessfuly. </p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEijf828RYBuoHtt0KEuRP-CPV3tOB6hTQIKX2Ov2soWg3jr7HrDwRIHl9Q3gJNWQpQMXtMD-8KJ72P6ZqgGtxSRmOscR-n0UDQTVcjXnfQkF4YS8pz-c54jDqx41DzVedyIRNxwclaFfruXqh3IPLKaFiaVrTmoyIhEuvck3lF866Qd1KouuiIMMWuE/s1680/21.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="879" data-original-width="1680" height="334" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEijf828RYBuoHtt0KEuRP-CPV3tOB6hTQIKX2Ov2soWg3jr7HrDwRIHl9Q3gJNWQpQMXtMD-8KJ72P6ZqgGtxSRmOscR-n0UDQTVcjXnfQkF4YS8pz-c54jDqx41DzVedyIRNxwclaFfruXqh3IPLKaFiaVrTmoyIhEuvck3lF866Qd1KouuiIMMWuE/w640-h334/21.png" width="640" /></a></div><br /><p>Objective of providing access to Azure functions based on subscriptions was acheieved. </p></div></div>Pratap Reddy Pilakahttp://www.blogger.com/profile/17141976956878163883noreply@blogger.com0tag:blogger.com,1999:blog-4208378482021219922.post-5825218324162836182022-03-11T21:45:00.005-08:002022-03-11T21:46:01.663-08:00React Js Modularity - Breaking a monolith react component into modular components<p>Earlier in 2017, I have written a few articles on <a href="https://pratapreddypilaka.blogspot.com/2017/07/basics-of-sharepoint-framework-webparts.html" target="_blank">Basics of SharePoint Framework</a>, <a href="https://pratapreddypilaka.blogspot.com/2017/11/react-component-basics.html" target="_blank">Use of React JS</a> and <a href="https://pratapreddypilaka.blogspot.com/2017/11/react-component-life-cycle.html" target="_blank">React JS component life cycle</a>. </p><p>But Most of the SPFx web parts I created were task oriented and not application oriented. This means the size of the React components is small. </p><p>Now I am working on a product / application level components which are complex and big in size.</p><p>Here is a screenshot of a POC I am currently working.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEj4Box-Jj7Zu0B8DKwoWAPAG6416vLokfKVEJyq_zgOkAmfKX7AvUmo46e7LVH3No5cMabIwpiiRDXQo44MXD4WQ0yqzJEZ9YHokG07HrtXP1oWTYVYZAHG2WphIWiSO7ZVTHBC5l_sQsl6hDODqs6rbbUaixGz1cO5E_lZiloCd24TDBMv3e_sRpte=s1787" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="696" data-original-width="1787" height="250" src="https://blogger.googleusercontent.com/img/a/AVvXsEj4Box-Jj7Zu0B8DKwoWAPAG6416vLokfKVEJyq_zgOkAmfKX7AvUmo46e7LVH3No5cMabIwpiiRDXQo44MXD4WQ0yqzJEZ9YHokG07HrtXP1oWTYVYZAHG2WphIWiSO7ZVTHBC5l_sQsl6hDODqs6rbbUaixGz1cO5E_lZiloCd24TDBMv3e_sRpte=w640-h250" width="640" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /><a name='more'></a></div>There are 3 Tabs, in which some tabs has roughly 4 to 5 forms with numerous fields. Now with what ever experience you have with SPFx and ReactJS development, imagine how big this component will be.<div><br /></div><div>If you are dealing with any react component with more than 300 lines of code, you need to think about breaking that monolith into small chunks.</div><div><br /></div><div>Its just not about no. of lines of code. Its about principle of "Separation of Concerns". I can envision above application as below.</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhCaKI0Znd1bFutgsMsvw0U0D7iUfrGQhFxfYEoY5kVXXZZ0AqBD4TEeUpjZB2zgusaknhUfSMJ-jEDWzXZGa0dAQR494aT2OUH-U7yo00ru94FoHlsiBis7_4CtYYPmXx0fWKBEyB5-2nAwhpXReQxc7o_AC_Q1GicSN9d-nXPRIOvXa0UZOXc5yFW=s863" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="537" data-original-width="863" height="398" src="https://blogger.googleusercontent.com/img/a/AVvXsEhCaKI0Znd1bFutgsMsvw0U0D7iUfrGQhFxfYEoY5kVXXZZ0AqBD4TEeUpjZB2zgusaknhUfSMJ-jEDWzXZGa0dAQR494aT2OUH-U7yo00ru94FoHlsiBis7_4CtYYPmXx0fWKBEyB5-2nAwhpXReQxc7o_AC_Q1GicSN9d-nXPRIOvXa0UZOXc5yFW=w640-h398" width="640" /></a></div><br /><div>Now question comes to how you going to separate functionality into different ReactJS components still make it work together.</div><div>There are 3 ways of doing it.</div><div><br /></div><div><b>1. Functions:</b> </div><div>Functions are used to separate small but reusable code that can be called in multiple places. Typical example would be headers and footers that can be written as functions and called in every page. below is the code sample how you do it in React.</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEiEQ152gK6DIM8YTSQGY7SLOc3P4bODCmi9ry4yDzus7rKOSi7JbhHMaqTC-vaFfFzit12FsA-ZNUVb7jtoHoiFd9m-4dDN3XS7N-PyzcT_4_e6Y38wIStb4QmlKx4ZxSiXz--ipGubwHSxC80y0iis4n_UjzItdkG56qqVXtOjEot8k3OkH92oDIRz=s927" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="453" data-original-width="927" height="312" src="https://blogger.googleusercontent.com/img/a/AVvXsEiEQ152gK6DIM8YTSQGY7SLOc3P4bODCmi9ry4yDzus7rKOSi7JbhHMaqTC-vaFfFzit12FsA-ZNUVb7jtoHoiFd9m-4dDN3XS7N-PyzcT_4_e6Y38wIStb4QmlKx4ZxSiXz--ipGubwHSxC80y0iis4n_UjzItdkG56qqVXtOjEot8k3OkH92oDIRz=w640-h312" width="640" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><a href="https://blogger.googleusercontent.com/img/a/AVvXsEiqGjY7d5e-LOhVbNDbfUJPoW95pOVmCMa434PI-XooNblXT_l4N_qIx9_bYiU73Pc979AadTVkIkAMLs39_F3sb1Vdbk7Kh9bcNFuC2ztyXsLtd_ykWrXdNLsBcdwO8JnbhOJ_cnXzHmuJugODjwIdghMUBsfNDmE71hMs9yXDsvsX524OlgDcZZ9i=s777" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="466" data-original-width="777" height="384" src="https://blogger.googleusercontent.com/img/a/AVvXsEiqGjY7d5e-LOhVbNDbfUJPoW95pOVmCMa434PI-XooNblXT_l4N_qIx9_bYiU73Pc979AadTVkIkAMLs39_F3sb1Vdbk7Kh9bcNFuC2ztyXsLtd_ykWrXdNLsBcdwO8JnbhOJ_cnXzHmuJugODjwIdghMUBsfNDmE71hMs9yXDsvsX524OlgDcZZ9i=w640-h384" width="640" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgeqmksDD-CVXEfDtEXQwOpHOGDaba5bGoSPHmMbEHzv8wol9Qo4qpoHsYvokcJNIdvfN4lB78FMzrkDF6dwgqunCN_UknrLwIdEBFgTPVATqpWC243vmUo3LeMUsLfY2y4Nb8Sy6gH1AnwR-Xw5X4WHOXG4kOt5y1jnDgIDmQpCRhgsRnxcvJbtX4e=s1010" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="307" data-original-width="1010" height="194" src="https://blogger.googleusercontent.com/img/a/AVvXsEgeqmksDD-CVXEfDtEXQwOpHOGDaba5bGoSPHmMbEHzv8wol9Qo4qpoHsYvokcJNIdvfN4lB78FMzrkDF6dwgqunCN_UknrLwIdEBFgTPVATqpWC243vmUo3LeMUsLfY2y4Nb8Sy6gH1AnwR-Xw5X4WHOXG4kOt5y1jnDgIDmQpCRhgsRnxcvJbtX4e=w640-h194" width="640" /></a></div><b>2. Sibling Components: </b><div>This means the the components are completely independent of their actions, and there is not data propagation between them. In the above example the components "Information" and "Membership" are sibling components. How do we embed them in UI Component?<br /><br /><div>Here are the definitions of both Information and Membership components.</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgGxwbBcHBi7lNcx6Lg21FUDlhQGeNjRDeo4uFPOiRVr03oOJTN30R0XUwLOkOpIpS1iItYd6p6WaFLqGuGL5zkJORKYxoSXOH3fiQB-nRO2jpSaZ5hKAdmnA6mzwmxXiBsfDIqZ6-yOGTyR47Vv5XM9nie5YSel_MwFUnfvxGNrR9jXfceUVv96OmM=s955" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="402" data-original-width="955" height="270" src="https://blogger.googleusercontent.com/img/a/AVvXsEgGxwbBcHBi7lNcx6Lg21FUDlhQGeNjRDeo4uFPOiRVr03oOJTN30R0XUwLOkOpIpS1iItYd6p6WaFLqGuGL5zkJORKYxoSXOH3fiQB-nRO2jpSaZ5hKAdmnA6mzwmxXiBsfDIqZ6-yOGTyR47Vv5XM9nie5YSel_MwFUnfvxGNrR9jXfceUVv96OmM=w640-h270" width="640" /></a></div><div><br /></div></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEh7fTFI07QjJMCQYhGRF6KicWPVkvJol5u0yaDiaR2sf6R_OreuierdMNbSZRcIPpMHieZQhlQC7PfqloJIb5V-1bh6NPm7b_yfl3wkaJPvTij4AOeeAbSMILCIa6bQbpd59XBO0Xu-TovUn5lXzxKTBCiCc6VERQP_vXc_SwD88sjTkxq0T-M1bCCh=s932" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="425" data-original-width="932" height="292" src="https://blogger.googleusercontent.com/img/a/AVvXsEh7fTFI07QjJMCQYhGRF6KicWPVkvJol5u0yaDiaR2sf6R_OreuierdMNbSZRcIPpMHieZQhlQC7PfqloJIb5V-1bh6NPm7b_yfl3wkaJPvTij4AOeeAbSMILCIa6bQbpd59XBO0Xu-TovUn5lXzxKTBCiCc6VERQP_vXc_SwD88sjTkxq0T-M1bCCh=w640-h292" width="640" /></a></div><br /><div>Now lets import these components into the UI component.</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhQn76L5-pJhP9blQrC-swBkeC9KwzXTj151qKDRwP4jv-Ump8nxui5bXop79GQGqHxZqWOxWk0euwofgKZBxRGsC6bzBrA5JsSnbYyAA8TH60L93VWZnX2udFrLHjAuW8kr361YnBYz82K2_za-uIE2TEyssYEHFUaKPH2wn1iWCmwUcD2aA9wUTfJ=s565" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="57" data-original-width="565" height="64" src="https://blogger.googleusercontent.com/img/a/AVvXsEhQn76L5-pJhP9blQrC-swBkeC9KwzXTj151qKDRwP4jv-Ump8nxui5bXop79GQGqHxZqWOxWk0euwofgKZBxRGsC6bzBrA5JsSnbYyAA8TH60L93VWZnX2udFrLHjAuW8kr361YnBYz82K2_za-uIE2TEyssYEHFUaKPH2wn1iWCmwUcD2aA9wUTfJ=w640-h64" width="640" /></a></div><br /><div>then this how we embed them in UI markup.</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjhTtqL1JH7aIxumT6055V723McNDSFG9QpiJywWvq11JGD6jKxLO_aKDwyjmLUHorJrXE0crh_PpnOHTLOZzYQG0ShmpwktOlQANyAxMetLAT6OPhvzOZ9sJ7CfEgYvZJsaJt2oXr8ny9X1x6JHOn_GJrZhMpL9Uv09uJ3faUrySBHdYqjIu3g2YE1=s532" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="135" data-original-width="532" height="162" src="https://blogger.googleusercontent.com/img/a/AVvXsEjhTtqL1JH7aIxumT6055V723McNDSFG9QpiJywWvq11JGD6jKxLO_aKDwyjmLUHorJrXE0crh_PpnOHTLOZzYQG0ShmpwktOlQANyAxMetLAT6OPhvzOZ9sJ7CfEgYvZJsaJt2oXr8ny9X1x6JHOn_GJrZhMpL9Uv09uJ3faUrySBHdYqjIu3g2YE1=w640-h162" width="640" /></a></div><br /><div>Here is the out come.</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhCbYjH_p-WugVWnCm0oUUtWqC742nJzvs3JWPWL580NOuALXi0VL2r8KQL3InE3_8vwVgsiy_AMoTpGmHrvosg6Hcimck16cGHkgcR3ZaP__yklofY3porS6JheC4y5fKYdliYB6_VcymG25es4w8CfrZqcOVCh7NaU5AJdGXwQmWjF2fM9p5QiEUq=s1497" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="482" data-original-width="1497" height="206" src="https://blogger.googleusercontent.com/img/a/AVvXsEhCbYjH_p-WugVWnCm0oUUtWqC742nJzvs3JWPWL580NOuALXi0VL2r8KQL3InE3_8vwVgsiy_AMoTpGmHrvosg6Hcimck16cGHkgcR3ZaP__yklofY3porS6JheC4y5fKYdliYB6_VcymG25es4w8CfrZqcOVCh7NaU5AJdGXwQmWjF2fM9p5QiEUq=w640-h206" width="640" /></a></div><br /><div><b>3. Parent Child Components:</b></div><div>Now in Template component , I have a Taxonomy picker control. For it to work, I need the Webpart Context. But you cannot find webpart context in child component. I need to send it from UI component.</div><div>How do we do that?</div><div><br /></div><div>Create a constructor in child component , which accepts the properties from parent.</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEiFFn_Loxjb1sLoqcpHUZR-eY1mLFMDhBFFyRVaITid8vKxMkMGhegKfX8PFPx1ywVVgDi4D_VQTT3rfD_bqS9EiVbfcwiee9ACQEUaXN5VxjoHWtvCVb-EnFPJT4Jm11jl4_rW4qvJmrYjJMxIrJTHDV5v_qASbYFgU6hTKsR9Hf2MB9kdd_sy542G=s865" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="361" data-original-width="865" height="268" src="https://blogger.googleusercontent.com/img/a/AVvXsEiFFn_Loxjb1sLoqcpHUZR-eY1mLFMDhBFFyRVaITid8vKxMkMGhegKfX8PFPx1ywVVgDi4D_VQTT3rfD_bqS9EiVbfcwiee9ACQEUaXN5VxjoHWtvCVb-EnFPJT4Jm11jl4_rW4qvJmrYjJMxIrJTHDV5v_qASbYFgU6hTKsR9Hf2MB9kdd_sy542G=w640-h268" width="640" /></a></div><br /><div>Now pass that webpart Context to Taxonomy picker.</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgQWOkRhN7JPKf3jX28BY9oUJlpUZ6K1fNB117MuHtEU_F3VbcRcZwkR1_ilPefeVSDpzEWNtCVvQLMbrKWSBU5km-kQFdKwFSW7RtuXrFr_KisHiWANq9PhZEBQrJ3AtmUqe41_3Ou50xCHOfzEdvUMueHebqiPmCXpZqXZyYYfPbhMp-5cN0nnAHQ=s767" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="346" data-original-width="767" height="288" src="https://blogger.googleusercontent.com/img/a/AVvXsEgQWOkRhN7JPKf3jX28BY9oUJlpUZ6K1fNB117MuHtEU_F3VbcRcZwkR1_ilPefeVSDpzEWNtCVvQLMbrKWSBU5km-kQFdKwFSW7RtuXrFr_KisHiWANq9PhZEBQrJ3AtmUqe41_3Ou50xCHOfzEdvUMueHebqiPmCXpZqXZyYYfPbhMp-5cN0nnAHQ=w640-h288" width="640" /></a></div><br /><div>In UI Component, this is how we send the Webpart Context to child component.</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjmYG4Dullqf8wVeWNqSqdreZF2_30l98k44jV9tOvjkvFiTY7zGjztaXEw4nOhX4VOaX-OMef5S3eYSelZ92i-wU8yDBJGTiVMICKc0XmPkBIDYWed58g1n0oTBbGu_fnG71BNr_pbcgeRYMtrZpaTf1bQM-Uvz8t0rMPJu-VXvLAjtDjqOfRIF-WF=s545" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="236" data-original-width="545" height="278" src="https://blogger.googleusercontent.com/img/a/AVvXsEjmYG4Dullqf8wVeWNqSqdreZF2_30l98k44jV9tOvjkvFiTY7zGjztaXEw4nOhX4VOaX-OMef5S3eYSelZ92i-wU8yDBJGTiVMICKc0XmPkBIDYWed58g1n0oTBbGu_fnG71BNr_pbcgeRYMtrZpaTf1bQM-Uvz8t0rMPJu-VXvLAjtDjqOfRIF-WF=w640-h278" width="640" /></a></div><br /><div>Now look at the out put</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgNOGjYVTYHikXQCEy4GwePviWiS4wfKXkN3TKjBnXVvQ1uQduZKEloka84SpZIZzO0Z1sm3GpHC9gH_QPGAV0UR9eehX3KXmQYp3lzhQs-hEty3dXvfexYolRMZ4rnXK3JTQrAboUfFU46izc03jmPFBXj7RGtwzsC5pSrWnhfse0PNHUE0x6-SGuB=s1192" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="858" data-original-width="1192" height="460" src="https://blogger.googleusercontent.com/img/a/AVvXsEgNOGjYVTYHikXQCEy4GwePviWiS4wfKXkN3TKjBnXVvQ1uQduZKEloka84SpZIZzO0Z1sm3GpHC9gH_QPGAV0UR9eehX3KXmQYp3lzhQs-hEty3dXvfexYolRMZ4rnXK3JTQrAboUfFU46izc03jmPFBXj7RGtwzsC5pSrWnhfse0PNHUE0x6-SGuB=w640-h460" width="640" /></a></div><div><br /></div>This is how we break monolith React components modular components and increase maintainability of our applications.<div><br /></div><div>Happy coding !</div>Pratap Reddy Pilakahttp://www.blogger.com/profile/17141976956878163883noreply@blogger.com0tag:blogger.com,1999:blog-4208378482021219922.post-72986215258861013302022-02-10T12:57:00.013-08:002022-02-13T12:26:04.028-08:00Azure PIM Provisioning and Configuration<h1 style="text-align: left;"><span style="font-size: large;">Setting up PIM
Administrator</span></h1>
<p class="MsoNormal"><span lang="EN-US" style="mso-ansi-language: EN-US;">Global
Admins enable PIM provisioning and create PIM Admin role assignment.<o:p></o:p></span></p>
<h4 style="text-align: left;"><span lang="EN-US" style="mso-ansi-language: EN-US;">PIM Admin Account
Pre-requisites: </span></h4>
<p class="MsoNormal"><span lang="EN-US" style="mso-ansi-language: EN-US;">PIM admin
account need to have below 2 licenses assigned.<o:p></o:p></span></p>
<p class="MsoListParagraphCxSpFirst" style="mso-list: l0 level1 lfo2; text-indent: -18pt;"></p><ol style="text-align: left;"><li><span lang="EN-US" style="mso-ansi-language: EN-US; mso-bidi-font-family: Calibri; mso-bidi-theme-font: minor-latin;"><span style="mso-list: Ignore;"><span style="font: 7pt "Times New Roman";"> </span></span></span><!--[endif]--><span lang="EN-US" style="mso-ansi-language: EN-US;">Azure AD Premium P2</span></li><li><span style="font-size: 7pt; font-stretch: normal; font-variant-east-asian: normal; font-variant-numeric: normal; line-height: normal;"> </span><span lang="EN-US">Enterprise Mobility + Security (EMS)
E5</span></li></ol><!--[if !supportLists]--><p></p>
<h4 style="text-align: left;"><span lang="EN-US" style="mso-ansi-language: EN-US;">PIM Admin Setup: </span></h4>
<p class="MsoListParagraphCxSpFirst"><!--[if !supportLists]--><span lang="EN-US" style="mso-ansi-language: EN-US; mso-bidi-font-family: Calibri; mso-bidi-theme-font: minor-latin;"><span style="mso-list: Ignore;">1</span></span><span lang="EN-US" style="mso-ansi-language: EN-US;">. Login to Azure portal as Global
admin, navigate to Azure Active Directory.<o:p></o:p></span></p>
<p class="MsoListParagraphCxSpMiddle"><!--[if !supportLists]--><span lang="EN-US" style="mso-ansi-language: EN-US; mso-bidi-font-family: Calibri; mso-bidi-theme-font: minor-latin;"><span style="mso-list: Ignore;">2.<span style="font: 7pt "Times New Roman";"> </span></span></span>In Featured highlights, click on <a href="https://blogger.googleusercontent.com/img/a/AVvXsEgZ9EwC1OaQHXJuJD660K7kwDjcMtuhcB5XLr_nUAqCpP-EDcJUpAhq1zcur8bqqi_s8UwFDV9nwK-C4gUMIZcsYdcX3IaFrqpYNse4z29zbHbvsvmMvsEG8w6TOCb7nXFx4JP5sgJ5nnQVEZ0SbXM8HTWH3nz_zw4DqTMnZJsf3vigZ0o_IE2VHwsa=s352"><img border="0" src="https://blogger.googleusercontent.com/img/a/AVvXsEgZ9EwC1OaQHXJuJD660K7kwDjcMtuhcB5XLr_nUAqCpP-EDcJUpAhq1zcur8bqqi_s8UwFDV9nwK-C4gUMIZcsYdcX3IaFrqpYNse4z29zbHbvsvmMvsEG8w6TOCb7nXFx4JP5sgJ5nnQVEZ0SbXM8HTWH3nz_zw4DqTMnZJsf3vigZ0o_IE2VHwsa=s320" /></a><br />3. Click on “Azure AD roles” in left pane=> Navigate to “Roles” by clicking on <a href="https://blogger.googleusercontent.com/img/a/AVvXsEgy8z31NPCVF2LTaRGF4FqMe_icXA_7wmCAbyDYYVz5x6kn4nsMI2bmngdX15QNvl-gGibO2foogKSc0LQHjy46qtKwU492ki6K2ds6KMXUu5cKxB9Z7nixLGLqUmlC9aafHSgkOPNBRDQ_RKSHOpBJqgiycfxTxoFVrhUb9kOsYWPyQOaYrS8gfPVA=s85"><img border="0" src="https://blogger.googleusercontent.com/img/a/AVvXsEgy8z31NPCVF2LTaRGF4FqMe_icXA_7wmCAbyDYYVz5x6kn4nsMI2bmngdX15QNvl-gGibO2foogKSc0LQHjy46qtKwU492ki6K2ds6KMXUu5cKxB9Z7nixLGLqUmlC9aafHSgkOPNBRDQ_RKSHOpBJqgiycfxTxoFVrhUb9kOsYWPyQOaYrS8gfPVA" /></a><br />4. Search for “privileged role administrator”. <br /><br /> <a href="https://blogger.googleusercontent.com/img/a/AVvXsEggNJMQW-zTAOiKwjxvpa6fW-9BU2i7pksxTD6t50pLtMhRr6TD9lw0aIPrxZDC-RzsWKyF7M8nsNfrKKZyp7jck89VIfsYTEcvh_1iutYR0aSvpzj6gTt8Ln-gFE-n_zomB4B-TBrssBQPt5YaYPKguBHfpdrXeuOMVOCSm0OnmTGNdpEUUcyiypYR=s357"><img border="0" src="https://blogger.googleusercontent.com/img/a/AVvXsEggNJMQW-zTAOiKwjxvpa6fW-9BU2i7pksxTD6t50pLtMhRr6TD9lw0aIPrxZDC-RzsWKyF7M8nsNfrKKZyp7jck89VIfsYTEcvh_1iutYR0aSvpzj6gTt8Ln-gFE-n_zomB4B-TBrssBQPt5YaYPKguBHfpdrXeuOMVOCSm0OnmTGNdpEUUcyiypYR=s320" /></a><br /><br />5. Click on “Privileged Role Administrator” role. Click on <a href="https://blogger.googleusercontent.com/img/a/AVvXsEjiOfS05wViDy0wLGmHxmSkaEhzHGfKaqhYu-iESn1br6fTTWt5FyL6QV0vgyYpAUQe1Z5-88AZ-z8nfhhi56B1SRGrWBMWZYaC5zJyc0vr_CFB0bwvs2hZLxHb9iqHzYzqLjg6mnbFx_lY1mEQ1QByQI2uu4FAVYdt4cYbf5ehXk9_5SWRVnTy6A3y=s140"><img border="0" src="https://blogger.googleusercontent.com/img/a/AVvXsEjiOfS05wViDy0wLGmHxmSkaEhzHGfKaqhYu-iESn1br6fTTWt5FyL6QV0vgyYpAUQe1Z5-88AZ-z8nfhhi56B1SRGrWBMWZYaC5zJyc0vr_CFB0bwvs2hZLxHb9iqHzYzqLjg6mnbFx_lY1mEQ1QByQI2uu4FAVYdt4cYbf5ehXk9_5SWRVnTy6A3y" /></a><br />6. Follow<span style="text-indent: -18pt;"> below configuration</span></p>
<table border="1" cellpadding="0" cellspacing="0" class="MsoTableGrid" style="border-collapse: collapse; border: none; margin-left: 36pt; mso-border-alt: solid windowtext .5pt; mso-padding-alt: 0cm 5.4pt 0cm 5.4pt; mso-yfti-tbllook: 1184;">
<tbody><tr style="mso-yfti-firstrow: yes; mso-yfti-irow: 0;">
<td style="border: 1pt solid windowtext; mso-border-alt: solid windowtext .5pt; padding: 0cm 5.4pt; width: 149.15pt;" valign="top" width="199">
<p class="MsoListParagraphCxSpMiddle" style="line-height: normal; margin: 0cm; mso-add-space: auto;"><span lang="EN-US" style="mso-ansi-language: EN-US;">Field<o:p></o:p></span></p>
</td>
<td style="border-left: none; border: 1pt solid windowtext; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; padding: 0cm 5.4pt; width: 141.5pt;" valign="top" width="189">
<p class="MsoListParagraphCxSpMiddle" style="line-height: normal; margin: 0cm; mso-add-space: auto;"><span lang="EN-US" style="mso-ansi-language: EN-US;">Value<o:p></o:p></span></p>
</td>
<td style="border-left: none; border: 1pt solid windowtext; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; padding: 0cm 5.4pt; width: 124.15pt;" valign="top" width="166">
<p class="MsoListParagraphCxSpLast" style="line-height: normal; margin: 0cm; mso-add-space: auto;"><span lang="EN-US" style="mso-ansi-language: EN-US;">Reason<o:p></o:p></span></p>
</td>
</tr>
<tr style="mso-yfti-irow: 1;">
<td style="border-top: none; border: 1pt solid windowtext; mso-border-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt; width: 149.15pt;" valign="top" width="199">
<p class="MsoListParagraphCxSpFirst" style="line-height: normal; margin: 0cm; mso-add-space: auto;"><span lang="EN-US" style="mso-ansi-language: EN-US;">Selected
Member(s)*<o:p></o:p></span></p>
</td>
<td style="border-bottom: 1pt solid windowtext; border-left: none; border-right: 1pt solid windowtext; border-top: none; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt; width: 141.5pt;" valign="top" width="189">
<p class="MsoListParagraphCxSpMiddle" style="line-height: normal; margin: 0cm; mso-add-space: auto;"><span lang="EN-US" style="mso-ansi-language: EN-US;">PIM
Admin Account<o:p></o:p></span></p>
</td>
<td style="border-bottom: 1pt solid windowtext; border-left: none; border-right: 1pt solid windowtext; border-top: none; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt; width: 124.15pt;" valign="top" width="166">
<p class="MsoListParagraphCxSpLast" style="line-height: normal; margin: 0cm; mso-add-space: auto;"><span lang="EN-US" style="mso-ansi-language: EN-US;">This
should be an account which will be permanently treated as PIM admin<o:p></o:p></span></p>
</td>
</tr>
<tr style="mso-yfti-irow: 2;">
<td style="border-top: none; border: 1pt solid windowtext; mso-border-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt; width: 149.15pt;" valign="top" width="199">
<p class="MsoListParagraphCxSpFirst" style="line-height: normal; margin: 0cm; mso-add-space: auto;"><span lang="EN-US" style="mso-ansi-language: EN-US;">Assignment
type<o:p></o:p></span></p>
</td>
<td style="border-bottom: 1pt solid windowtext; border-left: none; border-right: 1pt solid windowtext; border-top: none; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt; width: 141.5pt;" valign="top" width="189">
<p class="MsoListParagraphCxSpMiddle" style="line-height: normal; margin: 0cm; mso-add-space: auto;"><span lang="EN-US" style="mso-ansi-language: EN-US;">Eligible<o:p></o:p></span></p>
</td>
<td style="border-bottom: 1pt solid windowtext; border-left: none; border-right: 1pt solid windowtext; border-top: none; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt; width: 124.15pt;" valign="top" width="166">
<p class="MsoListParagraphCxSpLast" style="line-height: normal; margin: 0cm; mso-add-space: auto;"><span lang="EN-US" style="mso-ansi-language: EN-US;">This
means PIM admin account is always eligible, but not active. PIM admin need to
activate this role every time the changes need to be made to PIM
configuration <o:p></o:p></span></p>
</td>
</tr>
<tr style="mso-yfti-irow: 3; mso-yfti-lastrow: yes;">
<td style="border-top: none; border: 1pt solid windowtext; mso-border-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt; width: 149.15pt;" valign="top" width="199">
<p class="MsoListParagraphCxSpFirst" style="line-height: normal; margin: 0cm; mso-add-space: auto;"><span lang="EN-US" style="mso-ansi-language: EN-US;">Permanently
eligible<o:p></o:p></span></p>
</td>
<td style="border-bottom: 1pt solid windowtext; border-left: none; border-right: 1pt solid windowtext; border-top: none; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt; width: 141.5pt;" valign="top" width="189">
<p class="MsoListParagraphCxSpMiddle" style="line-height: normal; margin: 0cm; mso-add-space: auto;"><span lang="EN-US" style="mso-ansi-language: EN-US;">YES<o:p></o:p></span></p>
</td>
<td style="border-bottom: 1pt solid windowtext; border-left: none; border-right: 1pt solid windowtext; border-top: none; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt; width: 124.15pt;" valign="top" width="166">
<p class="MsoListParagraphCxSpLast" style="line-height: normal; margin: 0cm; mso-add-space: auto;"><span lang="EN-US" style="mso-ansi-language: EN-US;">Always
eligible, but not active.<o:p></o:p></span></p>
</td>
</tr>
</tbody></table>
<p class="MsoListParagraphCxSpFirst"><span lang="EN-US" style="mso-ansi-language: EN-US;"><o:p> </o:p></span><span lang="EN-US">7.<span style="font-size: 7pt; font-stretch: normal; font-variant-east-asian: normal; font-variant-numeric: normal; line-height: normal;"> </span></span>PIM Admin setup is finished.</p>
<a name='more'></a>
<h2 style="text-align: left;"><br /></h2><h2 style="text-align: left;">PIM Provisioning</h2><p class="MsoNormal"><span lang="EN-US" style="mso-ansi-language: EN-US;">PIM Admin
will be enabling which roles need to be enabled for PIM Access requests and
which users are eligible for PIM requests.<o:p></o:p></span></p>
<h4 style="text-align: left;"><span lang="EN-US" style="mso-ansi-language: EN-US;">Enabling PIM Admin Role </span></h4>
<h4 style="text-align: left;"><span lang="EN-US" style="mso-ansi-language: EN-US;">Setup:</span></h4>
<p class="MsoListParagraphCxSpFirst"><!--[if !supportLists]--><span lang="EN-US" style="mso-ansi-language: EN-US; mso-bidi-font-family: Calibri; mso-bidi-theme-font: minor-latin;"><span style="mso-list: Ignore;">1.<span style="font: 7pt "Times New Roman";"> </span></span></span>Login to Azure portal as PIM Adin account. Navigate to Azure Active Directory. <br />2. In Featured highlights, click on <a href="https://blogger.googleusercontent.com/img/a/AVvXsEh94ybZMN9yB55nouLOT8Ph3zIjAZejJf1Aq-iBXRNiK4dYvzS9ib8AagR5eJ2a8Eo7m-n2EojFLgRGNS1R0l7tIUJgwYbYjV4qVRlSZtbGce1NPkVZDHO-2WzDcJU8q9ak8J6GgWxtMPiXcMPPDi2mrEN4YwFJRB09VglEv_uaoDZEg8dXsVsncW8-=s352"><img border="0" src="https://blogger.googleusercontent.com/img/a/AVvXsEh94ybZMN9yB55nouLOT8Ph3zIjAZejJf1Aq-iBXRNiK4dYvzS9ib8AagR5eJ2a8Eo7m-n2EojFLgRGNS1R0l7tIUJgwYbYjV4qVRlSZtbGce1NPkVZDHO-2WzDcJU8q9ak8J6GgWxtMPiXcMPPDi2mrEN4YwFJRB09VglEv_uaoDZEg8dXsVsncW8-=s320" /></a><br />3. Click on “My Roles” in left pane. <a href="https://blogger.googleusercontent.com/img/a/AVvXsEgc8w57atYm2el0zKHvejAtP6nLTzfWr3GclIWiY9iNDlQzFVF-z05hNcJFtE1J4ocr29zW7sqQDVGE2sB1Cx1k3HBmAH0Hk-6LGHkmy9L1GqLRJ4lCCoKkFPl-HjI-m6GBUtY71mWJbHC4JuQ5E39zqraP6BltBnFeXeuV7RPUjyByd9urcKQxe4hi=s103"><img border="0" src="https://blogger.googleusercontent.com/img/a/AVvXsEgc8w57atYm2el0zKHvejAtP6nLTzfWr3GclIWiY9iNDlQzFVF-z05hNcJFtE1J4ocr29zW7sqQDVGE2sB1Cx1k3HBmAH0Hk-6LGHkmy9L1GqLRJ4lCCoKkFPl-HjI-m6GBUtY71mWJbHC4JuQ5E39zqraP6BltBnFeXeuV7RPUjyByd9urcKQxe4hi" /></a><br />Here you will see all the roles for which current user is eligible for. As per above configuration “Privileged Role Administrator” role should be available permanently. <br /><br />4. Click “Activate” button. User need to validate his credentials via Authenticator app by clicking on <br /><a href="https://blogger.googleusercontent.com/img/a/AVvXsEihvOHsLDVBVrkB6Nd4m8KWVaW81MgFIfrf96OvyEZeypU0kYFxbpmS5JGDhc2LcV-IUQ6nRJlnyyXkI8ib-bvNSqeVIJD67eIvzpx95UYoayY1lMWR7WRszYwDk2CaFgWkxneHvI2RVYSLH-3EvnJ2HCsZCnEVAPz4P_1TtxC5iNoy3djwRMuzzSy4=s322"><img border="0" src="https://blogger.googleusercontent.com/img/a/AVvXsEihvOHsLDVBVrkB6Nd4m8KWVaW81MgFIfrf96OvyEZeypU0kYFxbpmS5JGDhc2LcV-IUQ6nRJlnyyXkI8ib-bvNSqeVIJD67eIvzpx95UYoayY1lMWR7WRszYwDk2CaFgWkxneHvI2RVYSLH-3EvnJ2HCsZCnEVAPz4P_1TtxC5iNoy3djwRMuzzSy4=s320" /></a><br />5. Now the Duration slider will be enabled, and user can select how many hours the PIM admin role need to be activated. <br /><a href="https://blogger.googleusercontent.com/img/a/AVvXsEj__wgPlhxS2OpdSy6XvTm4y0umogIpt5vx6s-a41a-mBKnT0LwPvlbv9mg28UTGDEu1X472kcSmgHxoBTFkXKHLUvPVADtFKem1Z9ionjnsf_xwMLgnSIL5pgkNk9T_niwpnLdrNHaXmJvqI8A5rsUANJovav1v_hRO0yppF9yaL_xHhcZS8LCdHkJ=s306"><img border="0" height="188" src="https://blogger.googleusercontent.com/img/a/AVvXsEj__wgPlhxS2OpdSy6XvTm4y0umogIpt5vx6s-a41a-mBKnT0LwPvlbv9mg28UTGDEu1X472kcSmgHxoBTFkXKHLUvPVADtFKem1Z9ionjnsf_xwMLgnSIL5pgkNk9T_niwpnLdrNHaXmJvqI8A5rsUANJovav1v_hRO0yppF9yaL_xHhcZS8LCdHkJ=w400-h188" width="400" /></a><br /><br />6. PIM Ad<span style="text-indent: -18pt;">min role is active now.</span><span style="text-indent: -18pt;"> </span></p>
<h2 style="text-align: left;"><span lang="EN-US" style="mso-ansi-language: EN-US;"><br /></span></h2><h1 style="text-align: left;"><span lang="EN-US" style="mso-ansi-language: EN-US;">Configuring PIM constraints
for AD Roles</span></h1>
<p class="MsoNormal"><span lang="EN-US" style="mso-ansi-language: EN-US;">In this
document we intend to enable below mentioned AD Roles for PIM requests.<o:p></o:p></span></p>
<p class="MsoListParagraphCxSpFirst"><!--[if !supportLists]--><span lang="EN-US" style="mso-ansi-language: EN-US; mso-bidi-font-family: Calibri; mso-bidi-theme-font: minor-latin;"><span style="mso-list: Ignore;">1.<span style="font: 7pt "Times New Roman";"> </span></span></span>SharePoint Administrator <br />2. Teams Administrator <br />3. Excha<span style="text-indent: -18pt;">nge Administrator</span></p>
<h4 style="text-align: left;"><span lang="EN-US" style="mso-ansi-language: EN-US;">Setup:</span></h4>
<p class="MsoListParagraphCxSpFirst"><!--[if !supportLists]--><span lang="EN-US" style="mso-ansi-language: EN-US; mso-bidi-font-family: Calibri; mso-bidi-theme-font: minor-latin;"><span style="mso-list: Ignore;">1.</span></span>As PIM admin user log into Azure Portal, go to Azure Active Directory, navigate to “Privileged Identity Management”. <br />2. Click on “AD Roles” => Navigate to “Settings” in left pane. <br />3. Search for SharePoint Administrator role. Click on it. Now Click “Edit” Button to set the configuration. <br />4. Fo<span style="text-indent: -18pt;">llow below configuration</span></p>
<table border="1" cellpadding="0" cellspacing="0" class="MsoTableGrid" style="border-collapse: collapse; border: none; margin-left: 36pt; mso-border-alt: solid windowtext .5pt; mso-padding-alt: 0cm 5.4pt 0cm 5.4pt; mso-yfti-tbllook: 1184;">
<tbody><tr style="mso-yfti-firstrow: yes; mso-yfti-irow: 0;">
<td style="border: 1pt solid windowtext; mso-border-alt: solid windowtext .5pt; padding: 0cm 5.4pt; width: 150.25pt;" valign="top" width="200">
<p class="MsoListParagraphCxSpMiddle" style="line-height: normal; margin: 0cm; mso-add-space: auto;"><span lang="EN-US" style="mso-ansi-language: EN-US;">Field<o:p></o:p></span></p>
</td>
<td style="border-left: none; border: 1pt solid windowtext; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; padding: 0cm 5.4pt; width: 150.25pt;" valign="top" width="200">
<p class="MsoListParagraphCxSpMiddle" style="line-height: normal; margin: 0cm; mso-add-space: auto;"><span lang="EN-US" style="mso-ansi-language: EN-US;">Value<o:p></o:p></span></p>
</td>
<td style="border-left: none; border: 1pt solid windowtext; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; padding: 0cm 5.4pt; width: 150.3pt;" valign="top" width="200">
<p class="MsoListParagraphCxSpLast" style="line-height: normal; margin: 0cm; mso-add-space: auto;"><span lang="EN-US" style="mso-ansi-language: EN-US;">Reason<o:p></o:p></span></p>
</td>
</tr>
<tr style="mso-yfti-irow: 1;">
<td style="border-top: none; border: 1pt solid windowtext; mso-border-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt; width: 150.25pt;" valign="top" width="200">
<p class="MsoListParagraphCxSpFirst" style="line-height: normal; margin: 0cm; mso-add-space: auto;"><span lang="EN-US" style="mso-ansi-language: EN-US;">Activation
Maximum Duration (Hours)<o:p></o:p></span></p>
</td>
<td style="border-bottom: 1pt solid windowtext; border-left: none; border-right: 1pt solid windowtext; border-top: none; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt; width: 150.25pt;" valign="top" width="200">
<p class="MsoListParagraphCxSpMiddle" style="line-height: normal; margin: 0cm; mso-add-space: auto;"><span lang="EN-US" style="mso-ansi-language: EN-US;">4<o:p></o:p></span></p>
</td>
<td style="border-bottom: 1pt solid windowtext; border-left: none; border-right: 1pt solid windowtext; border-top: none; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt; width: 150.3pt;" valign="top" width="200">
<p class="MsoListParagraphCxSpLast" style="line-height: normal; margin: 0cm; mso-add-space: auto;"><span lang="EN-US" style="mso-ansi-language: EN-US;">PIM Role
Activation will automatically be disabled after 4 hours<o:p></o:p></span></p>
</td>
</tr>
<tr style="mso-yfti-irow: 2;">
<td style="border-top: none; border: 1pt solid windowtext; mso-border-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt; width: 150.25pt;" valign="top" width="200">
<p class="MsoListParagraphCxSpFirst" style="line-height: normal; margin: 0cm; mso-add-space: auto;"><span lang="EN-US" style="mso-ansi-language: EN-US;">On
Activation , require<o:p></o:p></span></p>
</td>
<td style="border-bottom: 1pt solid windowtext; border-left: none; border-right: 1pt solid windowtext; border-top: none; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt; width: 150.25pt;" valign="top" width="200">
<p class="MsoListParagraphCxSpMiddle" style="line-height: normal; margin: 0cm; mso-add-space: auto;"><span lang="EN-US" style="mso-ansi-language: EN-US;">Azure
MFA<o:p></o:p></span></p>
</td>
<td style="border-bottom: 1pt solid windowtext; border-left: none; border-right: 1pt solid windowtext; border-top: none; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt; width: 150.3pt;" valign="top" width="200">
<p class="MsoListParagraphCxSpLast" style="line-height: normal; margin: 0cm; mso-add-space: auto;"><span lang="EN-US" style="mso-ansi-language: EN-US;">PIM role
will not be activated without Authenticator MFA approval.<o:p></o:p></span></p>
</td>
</tr>
<tr style="mso-yfti-irow: 3;">
<td style="border-top: none; border: 1pt solid windowtext; mso-border-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt; width: 150.25pt;" valign="top" width="200">
<p class="MsoListParagraphCxSpFirst" style="line-height: normal; margin: 0cm; mso-add-space: auto;"><span lang="EN-US" style="mso-ansi-language: EN-US;">Require
Justification on Activation<o:p></o:p></span></p>
</td>
<td style="border-bottom: 1pt solid windowtext; border-left: none; border-right: 1pt solid windowtext; border-top: none; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt; width: 150.25pt;" valign="top" width="200">
<p class="MsoListParagraphCxSpMiddle" style="line-height: normal; margin: 0cm; mso-add-space: auto;"><span lang="EN-US" style="mso-ansi-language: EN-US;">True /
Yes<o:p></o:p></span></p>
</td>
<td style="border-bottom: 1pt solid windowtext; border-left: none; border-right: 1pt solid windowtext; border-top: none; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt; width: 150.3pt;" valign="top" width="200">
<p class="MsoListParagraphCxSpLast" style="line-height: normal; margin: 0cm; mso-add-space: auto;"><span lang="EN-US" style="mso-ansi-language: EN-US;">Will
make Justification txt field mandatory<o:p></o:p></span></p>
</td>
</tr>
<tr style="mso-yfti-irow: 4;">
<td style="border-top: none; border: 1pt solid windowtext; mso-border-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt; width: 150.25pt;" valign="top" width="200">
<p class="MsoListParagraphCxSpFirst" style="line-height: normal; margin: 0cm; mso-add-space: auto;"><span lang="EN-US" style="mso-ansi-language: EN-US;">Require
ticket information on activation<o:p></o:p></span></p>
</td>
<td style="border-bottom: 1pt solid windowtext; border-left: none; border-right: 1pt solid windowtext; border-top: none; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt; width: 150.25pt;" valign="top" width="200">
<p class="MsoListParagraphCxSpMiddle" style="line-height: normal; margin: 0cm; mso-add-space: auto;"><span lang="EN-US" style="mso-ansi-language: EN-US;">Ture/
Yes<o:p></o:p></span></p>
</td>
<td style="border-bottom: 1pt solid windowtext; border-left: none; border-right: 1pt solid windowtext; border-top: none; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt; width: 150.3pt;" valign="top" width="200">
<p class="MsoListParagraphCxSpLast" style="line-height: normal; margin: 0cm; mso-add-space: auto;"><span lang="EN-US" style="mso-ansi-language: EN-US;">Will
make ticket / SR field mandatory <o:p></o:p></span></p>
</td>
</tr>
<tr style="mso-yfti-irow: 5;">
<td style="border-top: none; border: 1pt solid windowtext; mso-border-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt; width: 150.25pt;" valign="top" width="200">
<p class="MsoListParagraphCxSpFirst" style="line-height: normal; margin: 0cm; mso-add-space: auto;"><span lang="EN-US" style="mso-ansi-language: EN-US;">Require
Approval to activate <o:p></o:p></span></p>
</td>
<td style="border-bottom: 1pt solid windowtext; border-left: none; border-right: 1pt solid windowtext; border-top: none; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt; width: 150.25pt;" valign="top" width="200">
<p class="MsoListParagraphCxSpMiddle" style="line-height: normal; margin: 0cm; mso-add-space: auto;"><span lang="EN-US" style="mso-ansi-language: EN-US;">False /
No<o:p></o:p></span></p>
</td>
<td style="border-bottom: 1pt solid windowtext; border-left: none; border-right: 1pt solid windowtext; border-top: none; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt; width: 150.3pt;" valign="top" width="200">
<p class="MsoListParagraphCxSpLast" style="line-height: normal; margin: 0cm; mso-add-space: auto;"><span lang="EN-US" style="mso-ansi-language: EN-US;">No
approvals necessary as of now<o:p></o:p></span></p>
</td>
</tr>
<tr style="mso-yfti-irow: 6;">
<td style="border-top: none; border: 1pt solid windowtext; mso-border-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt; width: 150.25pt;" valign="top" width="200">
<p class="MsoListParagraphCxSpFirst" style="line-height: normal; margin: 0cm; mso-add-space: auto;"><span lang="EN-US" style="mso-ansi-language: EN-US;">Allow
Permanent eligibility assignment<o:p></o:p></span></p>
</td>
<td style="border-bottom: 1pt solid windowtext; border-left: none; border-right: 1pt solid windowtext; border-top: none; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt; width: 150.25pt;" valign="top" width="200">
<p class="MsoListParagraphCxSpMiddle" style="line-height: normal; margin: 0cm; mso-add-space: auto;"><span lang="EN-US" style="mso-ansi-language: EN-US;">False /
No<o:p></o:p></span></p>
</td>
<td style="border-bottom: 1pt solid windowtext; border-left: none; border-right: 1pt solid windowtext; border-top: none; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt; width: 150.3pt;" valign="top" width="200">
<p class="MsoListParagraphCxSpLast" style="line-height: normal; margin: 0cm; mso-add-space: auto;"><span lang="EN-US" style="mso-ansi-language: EN-US;">We want
eligibility assignment only for a year.<o:p></o:p></span></p>
</td>
</tr>
<tr style="mso-yfti-irow: 7;">
<td style="border-top: none; border: 1pt solid windowtext; mso-border-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt; width: 150.25pt;" valign="top" width="200">
<p class="MsoListParagraphCxSpFirst" style="line-height: normal; margin: 0cm; mso-add-space: auto;"><span lang="EN-US" style="mso-ansi-language: EN-US;">Expire
Eligibility assignments after<o:p></o:p></span></p>
</td>
<td style="border-bottom: 1pt solid windowtext; border-left: none; border-right: 1pt solid windowtext; border-top: none; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt; width: 150.25pt;" valign="top" width="200">
<p class="MsoListParagraphCxSpMiddle" style="line-height: normal; margin: 0cm; mso-add-space: auto;"><span lang="EN-US" style="mso-ansi-language: EN-US;">1 Year<o:p></o:p></span></p>
</td>
<td style="border-bottom: 1pt solid windowtext; border-left: none; border-right: 1pt solid windowtext; border-top: none; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt; width: 150.3pt;" valign="top" width="200">
<p class="MsoListParagraphCxSpLast" style="line-height: normal; margin: 0cm; mso-add-space: auto;"><span lang="EN-US" style="mso-ansi-language: EN-US;">Eligibility
assignment for this role will be valid for an year.<o:p></o:p></span></p>
</td>
</tr>
<tr style="mso-yfti-irow: 8;">
<td style="border-top: none; border: 1pt solid windowtext; mso-border-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt; width: 150.25pt;" valign="top" width="200">
<p class="MsoListParagraphCxSpFirst" style="line-height: normal; margin: 0cm; mso-add-space: auto;"><span lang="EN-US" style="mso-ansi-language: EN-US;">Allow
Permanent active assignment<o:p></o:p></span></p>
</td>
<td style="border-bottom: 1pt solid windowtext; border-left: none; border-right: 1pt solid windowtext; border-top: none; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt; width: 150.25pt;" valign="top" width="200">
<p class="MsoListParagraphCxSpMiddle" style="line-height: normal; margin: 0cm; mso-add-space: auto;"><span lang="EN-US" style="mso-ansi-language: EN-US;">False /
No <o:p></o:p></span></p>
</td>
<td style="border-bottom: 1pt solid windowtext; border-left: none; border-right: 1pt solid windowtext; border-top: none; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt; width: 150.3pt;" valign="top" width="200">
<p class="MsoListParagraphCxSpLast" style="line-height: normal; margin: 0cm; mso-add-space: auto;"><span lang="EN-US" style="mso-ansi-language: EN-US;"><o:p> </o:p></span></p>
</td>
</tr>
<tr style="mso-yfti-irow: 9;">
<td style="border-top: none; border: 1pt solid windowtext; mso-border-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt; width: 150.25pt;" valign="top" width="200">
<p class="MsoListParagraphCxSpFirst" style="line-height: normal; margin: 0cm; mso-add-space: auto;"><span lang="EN-US" style="mso-ansi-language: EN-US;">Expire
active assignments after <o:p></o:p></span></p>
</td>
<td style="border-bottom: 1pt solid windowtext; border-left: none; border-right: 1pt solid windowtext; border-top: none; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt; width: 150.25pt;" valign="top" width="200">
<p class="MsoListParagraphCxSpMiddle" style="line-height: normal; margin: 0cm; mso-add-space: auto;"><span lang="EN-US" style="mso-ansi-language: EN-US;">15 Days<o:p></o:p></span></p>
</td>
<td style="border-bottom: 1pt solid windowtext; border-left: none; border-right: 1pt solid windowtext; border-top: none; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt; width: 150.3pt;" valign="top" width="200">
<p class="MsoListParagraphCxSpLast" style="line-height: normal; margin: 0cm; mso-add-space: auto;"><span lang="EN-US" style="mso-ansi-language: EN-US;">We need
to limit direct active assignments to minimum value so that they don’t
overstay its welcome.<o:p></o:p></span></p>
</td>
</tr>
<tr style="mso-yfti-irow: 10;">
<td style="border-top: none; border: 1pt solid windowtext; mso-border-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt; width: 150.25pt;" valign="top" width="200">
<p class="MsoListParagraphCxSpFirst" style="line-height: normal; margin: 0cm; mso-add-space: auto;"><span lang="EN-US" style="mso-ansi-language: EN-US;">Require
Azure Multi-Factor Authentication on active assignment<o:p></o:p></span></p>
</td>
<td style="border-bottom: 1pt solid windowtext; border-left: none; border-right: 1pt solid windowtext; border-top: none; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt; width: 150.25pt;" valign="top" width="200">
<p class="MsoListParagraphCxSpMiddle" style="line-height: normal; margin: 0cm; mso-add-space: auto;"><span lang="EN-US" style="mso-ansi-language: EN-US;">True /
Yes<o:p></o:p></span></p>
</td>
<td style="border-bottom: 1pt solid windowtext; border-left: none; border-right: 1pt solid windowtext; border-top: none; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt; width: 150.3pt;" valign="top" width="200">
<p class="MsoListParagraphCxSpLast" style="line-height: normal; margin: 0cm; mso-add-space: auto;"><span lang="EN-US" style="mso-ansi-language: EN-US;"><o:p> </o:p></span></p>
</td>
</tr>
<tr style="mso-yfti-irow: 11;">
<td style="border-top: none; border: 1pt solid windowtext; mso-border-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt; width: 150.25pt;" valign="top" width="200">
<p class="MsoListParagraphCxSpFirst" style="line-height: normal; margin: 0cm; mso-add-space: auto;"><span lang="EN-US" style="mso-ansi-language: EN-US;">Require Justification
on active assignment<o:p></o:p></span></p>
</td>
<td style="border-bottom: 1pt solid windowtext; border-left: none; border-right: 1pt solid windowtext; border-top: none; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt; width: 150.25pt;" valign="top" width="200">
<p class="MsoListParagraphCxSpMiddle" style="line-height: normal; margin: 0cm; mso-add-space: auto;"><span lang="EN-US" style="mso-ansi-language: EN-US;">True /
Yes<o:p></o:p></span></p>
</td>
<td style="border-bottom: 1pt solid windowtext; border-left: none; border-right: 1pt solid windowtext; border-top: none; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt; width: 150.3pt;" valign="top" width="200">
<p class="MsoListParagraphCxSpLast" style="line-height: normal; margin: 0cm; mso-add-space: auto;"><span lang="EN-US" style="mso-ansi-language: EN-US;"><o:p> </o:p></span></p>
</td>
</tr>
<tr style="mso-yfti-irow: 12; mso-yfti-lastrow: yes;">
<td style="border-top: none; border: 1pt solid windowtext; mso-border-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt; width: 150.25pt;" valign="top" width="200">
<p class="MsoListParagraphCxSpFirst" style="line-height: normal; margin: 0cm; mso-add-space: auto;"><span lang="EN-US" style="mso-ansi-language: EN-US;">Notification
<o:p></o:p></span></p>
</td>
<td style="border-bottom: 1pt solid windowtext; border-left: none; border-right: 1pt solid windowtext; border-top: none; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt; width: 150.25pt;" valign="top" width="200">
<p class="MsoListParagraphCxSpMiddle" style="line-height: normal; margin: 0cm; mso-add-space: auto;"><span lang="EN-US" style="mso-ansi-language: EN-US;">Enable
all emails and add any additional email IDs if required.<o:p></o:p></span></p>
</td>
<td style="border-bottom: 1pt solid windowtext; border-left: none; border-right: 1pt solid windowtext; border-top: none; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt; width: 150.3pt;" valign="top" width="200">
<p class="MsoListParagraphCxSpLast" style="line-height: normal; margin: 0cm; mso-add-space: auto;"><span lang="EN-US" style="mso-ansi-language: EN-US;"><o:p> </o:p></span></p>
</td>
</tr>
</tbody></table>
5. Now repeat steps 3 and 4 for remaining roles “Teams Administrator” and “Exchange Administrator”.
<p class="MsoListParagraphCxSpLast"><!--[if !supportLists]--><span lang="EN-US" style="mso-ansi-language: EN-US; mso-bidi-font-family: Calibri; mso-bidi-theme-font: minor-latin;"><span style="mso-list: Ignore;">6.</span></span><span lang="EN-US" style="mso-ansi-language: EN-US;">PIM Parameters for Ad role configuration
is completed. <o:p></o:p></span></p>
<h2 style="text-align: left;"><span lang="EN-US" style="mso-ansi-language: EN-US;"><br /></span></h2><h1 style="text-align: left;"><span lang="EN-US" style="mso-ansi-language: EN-US;">Enabling PIM Access for
Users / Groups</span></h1>
<p class="MsoNormal"><span lang="EN-US" style="mso-ansi-language: EN-US;">This step
is performed by PIM Admin by adding AD Users / AD Groups for each individual AD
Role.<o:p></o:p></span></p>
<h4 style="text-align: left;"><span lang="EN-US" style="mso-ansi-language: EN-US;">Setup:</span></h4>
<p class="MsoListParagraphCxSpFirst"><!--[if !supportLists]--><span lang="EN-US" style="mso-ansi-language: EN-US; mso-bidi-font-family: Calibri; mso-bidi-theme-font: minor-latin;"><span style="mso-list: Ignore;">1.</span></span>As PIM admin user log into Azure Portal, go to Azure Active Directory, navigate to “Privileged Identity Management”. <br />2. Click on “AD Roles” => Navigate to “Roles” => Search for “SharePoint Administrator” <br />3. Click<span style="text-indent: -18pt;"> on “Add Assignment”, follow
below configuration</span></p>
<table border="1" cellpadding="0" cellspacing="0" class="MsoTableGrid" style="border-collapse: collapse; border: none; margin-left: 36pt; mso-border-alt: solid windowtext .5pt; mso-padding-alt: 0cm 5.4pt 0cm 5.4pt; mso-yfti-tbllook: 1184;">
<tbody><tr style="mso-yfti-firstrow: yes; mso-yfti-irow: 0;">
<td style="border: 1pt solid windowtext; mso-border-alt: solid windowtext .5pt; padding: 0cm 5.4pt; width: 132.8pt;" valign="top" width="177">
<p class="MsoListParagraphCxSpMiddle" style="line-height: normal; margin: 0cm; mso-add-space: auto;"><span lang="EN-US" style="mso-ansi-language: EN-US;">Field<o:p></o:p></span></p>
</td>
<td style="border-left: none; border: 1pt solid windowtext; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; padding: 0cm 5.4pt; width: 147.5pt;" valign="top" width="197">
<p class="MsoListParagraphCxSpMiddle" style="line-height: normal; margin: 0cm; mso-add-space: auto;"><span lang="EN-US" style="mso-ansi-language: EN-US;">Value<o:p></o:p></span></p>
</td>
<td style="border-left: none; border: 1pt solid windowtext; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; padding: 0cm 5.4pt; width: 134.5pt;" valign="top" width="179">
<p class="MsoListParagraphCxSpLast" style="line-height: normal; margin: 0cm; mso-add-space: auto;"><span lang="EN-US" style="mso-ansi-language: EN-US;">Reason<o:p></o:p></span></p>
</td>
</tr>
<tr style="mso-yfti-irow: 1;">
<td style="border-top: none; border: 1pt solid windowtext; mso-border-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt; width: 132.8pt;" valign="top" width="177">
<p class="MsoListParagraphCxSpFirst" style="line-height: normal; margin: 0cm; mso-add-space: auto;"><span lang="EN-US" style="mso-ansi-language: EN-US;">Scope
type<o:p></o:p></span></p>
</td>
<td style="border-bottom: 1pt solid windowtext; border-left: none; border-right: 1pt solid windowtext; border-top: none; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt; width: 147.5pt;" valign="top" width="197">
<p class="MsoListParagraphCxSpMiddle" style="line-height: normal; margin: 0cm; mso-add-space: auto;"><span lang="EN-US" style="mso-ansi-language: EN-US;">Directory<o:p></o:p></span></p>
</td>
<td style="border-bottom: 1pt solid windowtext; border-left: none; border-right: 1pt solid windowtext; border-top: none; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt; width: 134.5pt;" valign="top" width="179">
<p class="MsoListParagraphCxSpLast" style="line-height: normal; margin: 0cm; mso-add-space: auto;"><span lang="EN-US" style="mso-ansi-language: EN-US;"><o:p> </o:p></span></p>
</td>
</tr>
<tr style="mso-yfti-irow: 2;">
<td style="border-top: none; border: 1pt solid windowtext; mso-border-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt; width: 132.8pt;" valign="top" width="177">
<p class="MsoListParagraphCxSpFirst" style="line-height: normal; margin: 0cm; mso-add-space: auto;"><span lang="EN-US" style="mso-ansi-language: EN-US;">Selected
member(s)*<o:p></o:p></span></p>
</td>
<td style="border-bottom: 1pt solid windowtext; border-left: none; border-right: 1pt solid windowtext; border-top: none; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt; width: 147.5pt;" valign="top" width="197">
<p class="MsoListParagraphCxSpMiddle" style="line-height: normal; margin: 0cm; mso-add-space: auto;"><span face=""Segoe UI", sans-serif" style="background-color: white; font-size: 12.6667px;">AzureSecurityGroupForPIMAccess</span></p>
</td>
<td style="border-bottom: 1pt solid windowtext; border-left: none; border-right: 1pt solid windowtext; border-top: none; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt; width: 134.5pt;" valign="top" width="179">
<p class="MsoListParagraphCxSpLast" style="line-height: normal; margin: 0cm; mso-add-space: auto;"><span lang="EN-US" style="mso-ansi-language: EN-US;">This AD
security group contains all the _admin accounts which requires elevated
privileges from time to time. <o:p></o:p></span></p>
</td>
</tr>
<tr style="mso-yfti-irow: 3;">
<td style="border-top: none; border: 1pt solid windowtext; mso-border-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt; width: 132.8pt;" valign="top" width="177">
<p class="MsoListParagraphCxSpFirst" style="line-height: normal; margin: 0cm; mso-add-space: auto;"><span lang="EN-US" style="mso-ansi-language: EN-US;">Assignment
type<o:p></o:p></span></p>
</td>
<td style="border-bottom: 1pt solid windowtext; border-left: none; border-right: 1pt solid windowtext; border-top: none; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt; width: 147.5pt;" valign="top" width="197">
<p class="MsoListParagraphCxSpMiddle" style="line-height: normal; margin: 0cm; mso-add-space: auto;"><span lang="EN-US" style="mso-ansi-language: EN-US;">Eligible<o:p></o:p></span></p>
</td>
<td style="border-bottom: 1pt solid windowtext; border-left: none; border-right: 1pt solid windowtext; border-top: none; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt; width: 134.5pt;" valign="top" width="179">
<p class="MsoListParagraphCxSpLast" style="line-height: normal; margin: 0cm; mso-add-space: auto;"><span lang="EN-US" style="mso-ansi-language: EN-US;">The
users in security group will be eligible for this role up on PIM access
request. This doesn’t mean it is activated.<o:p></o:p></span></p>
</td>
</tr>
<tr style="mso-yfti-irow: 4;">
<td style="border-top: none; border: 1pt solid windowtext; mso-border-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt; width: 132.8pt;" valign="top" width="177">
<p class="MsoListParagraphCxSpFirst" style="line-height: normal; margin: 0cm; mso-add-space: auto;"><span lang="EN-US" style="mso-ansi-language: EN-US;">Assignment
Starts<o:p></o:p></span></p>
</td>
<td style="border-bottom: 1pt solid windowtext; border-left: none; border-right: 1pt solid windowtext; border-top: none; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt; width: 147.5pt;" valign="top" width="197">
<p class="MsoListParagraphCxSpMiddle" style="line-height: normal; margin: 0cm; mso-add-space: auto;"><span lang="EN-US" style="mso-ansi-language: EN-US;">Current
date and time / Start date and time<o:p></o:p></span></p>
</td>
<td style="border-bottom: 1pt solid windowtext; border-left: none; border-right: 1pt solid windowtext; border-top: none; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt; width: 134.5pt;" valign="top" width="179">
<p class="MsoListParagraphCxSpLast" style="line-height: normal; margin: 0cm; mso-add-space: auto;"><span lang="EN-US" style="mso-ansi-language: EN-US;"><o:p> </o:p></span></p>
</td>
</tr>
<tr style="mso-yfti-irow: 5; mso-yfti-lastrow: yes;">
<td style="border-top: none; border: 1pt solid windowtext; mso-border-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt; width: 132.8pt;" valign="top" width="177">
<p class="MsoListParagraphCxSpFirst" style="line-height: normal; margin: 0cm; mso-add-space: auto;"><span lang="EN-US" style="mso-ansi-language: EN-US;">Assignment
ends<o:p></o:p></span></p>
</td>
<td style="border-bottom: 1pt solid windowtext; border-left: none; border-right: 1pt solid windowtext; border-top: none; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt; width: 147.5pt;" valign="top" width="197">
<p class="MsoListParagraphCxSpMiddle" style="line-height: normal; margin: 0cm; mso-add-space: auto;"><span lang="EN-US" style="mso-ansi-language: EN-US;">1 year
from now<o:p></o:p></span></p>
</td>
<td style="border-bottom: 1pt solid windowtext; border-left: none; border-right: 1pt solid windowtext; border-top: none; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt; width: 134.5pt;" valign="top" width="179">
<p class="MsoListParagraphCxSpLast" style="line-height: normal; margin: 0cm; mso-add-space: auto;"><span lang="EN-US" style="mso-ansi-language: EN-US;">1 year
is maximum eligibility as per above configuration.<o:p></o:p></span></p>
</td>
</tr>
</tbody></table>
<p class="MsoListParagraphCxSpFirst"><span lang="EN-US">4.</span>Now repeat steps 2 and 3 for remaining roles “Teams Administrator” and “Exchange Administrator”.</p><p class="MsoListParagraphCxSpMiddle">5. Addin<span style="text-indent: -18pt;">g eligible user for each AD
role PIM access is completed.</span></p>
<h1 style="text-align: left;"><br /></h1><h1 style="text-align: left;">PIM Usage</h1>
<h4 style="text-align: left;"><span lang="EN-US" style="mso-ansi-language: EN-US;">PIM User Request</span></h4>
<p class="MsoNormal"><span lang="EN-US" style="mso-ansi-language: EN-US;">This is the
process followed by Users to raise Just-in-time privileged access for various
AD roles.<o:p></o:p></span></p>
<h4 style="text-align: left;"><span lang="EN-US" style="mso-ansi-language: EN-US;">Pre-requisite:</span></h4>
<p class="MsoNormal"><span lang="EN-US" style="mso-ansi-language: EN-US;">User should
be part of the “</span><span face=""Segoe UI",sans-serif" style="background: white; color: black; font-size: 9.5pt; line-height: 107%;">AzureSecurityGroupForPIMAccess</span><span lang="EN-US" style="mso-ansi-language: EN-US;">” AD security group. <o:p></o:p></span></p>
<h4 style="text-align: left;"><span lang="EN-US" style="mso-ansi-language: EN-US;">Setup:</span></h4>
<p class="MsoListParagraphCxSpFirst"><!--[if !supportLists]--><span lang="EN-US" style="mso-ansi-language: EN-US; mso-bidi-font-family: Calibri; mso-bidi-theme-font: minor-latin;"><span style="mso-list: Ignore;">1.</span></span>User log on to azure portal. Navigate to Azure Active Directory <br />2. In Featured highlights, click on <a href="https://blogger.googleusercontent.com/img/a/AVvXsEgOmeiHrH86tleUrr3ALONK4CIL9xoDo7uWCFNhbbutum1n_xHBpc5ZzVzvRTnGI7loTMj_Rtqh3IioYfXthUOk1KYWG-wOWlHOvVEzxgwLYG5R9nqujjQomFyp3y7sKvVVcMj2e2xb8jXkHg2Rz3xlDjjbeVFbF2H48qfpwvSz0xHiwkwmS70Z1pmA=s352"><img border="0" src="https://blogger.googleusercontent.com/img/a/AVvXsEgOmeiHrH86tleUrr3ALONK4CIL9xoDo7uWCFNhbbutum1n_xHBpc5ZzVzvRTnGI7loTMj_Rtqh3IioYfXthUOk1KYWG-wOWlHOvVEzxgwLYG5R9nqujjQomFyp3y7sKvVVcMj2e2xb8jXkHg2Rz3xlDjjbeVFbF2H48qfpwvSz0xHiwkwmS70Z1pmA=s320" /></a><br />3. Click on “My Roles” in left pane. User will see all the roles for whom user is eligible for just-in-time elevated privileges. <br />4. Click “Activate” Button, User need to MFA authenticated to enable the activate pane. Below information should be provided to activate PIM access. <br /><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhVfghhus8uhr3taLbaHL-c33O9ivhVdJx705y43MPK7hN7VUtHOJHMxLSpsHiElXp-C6TcDiFJG5Rw4PAp9QCP5qxBAaVXjLwV0rgla-TU7FA3hNfkDOuOxhmU9W_d4oRMVebmxBYe2FCL9DDVyiDWmw-1M1qu1iwjc_2tB94a77CTRZZ3yDoHuUvv=s446"><img border="0" src="https://blogger.googleusercontent.com/img/a/AVvXsEhVfghhus8uhr3taLbaHL-c33O9ivhVdJx705y43MPK7hN7VUtHOJHMxLSpsHiElXp-C6TcDiFJG5Rw4PAp9QCP5qxBAaVXjLwV0rgla-TU7FA3hNfkDOuOxhmU9W_d4oRMVebmxBYe2FCL9DDVyiDWmw-1M1qu1iwjc_2tB94a77CTRZZ3yDoHuUvv=s320" /></a><br /><br />5. Just-in-time elevated privilege was activated successfully. This wil<span style="text-indent: -18pt;">l be active for next 4 hours. </span></p>Pratap Reddy Pilakahttp://www.blogger.com/profile/17141976956878163883noreply@blogger.com0tag:blogger.com,1999:blog-4208378482021219922.post-44421060566562850962021-05-24T08:23:00.001-07:002021-05-24T08:24:59.084-07:00Data Persistence Models in Docker Containers<p>A container has different layers starting with <b>Minimal Subset of OS</b> topped by <b>Container Filesystem</b> topped by <b>Application layer </b>topped by <b>Hosting layer. </b>All these layers are read-only.</p><p>There is a top layer called <b>Container Runtime layer </b>which will be in a Read/Write state. </p><p>The data on <b>Container</b> <b>Runtime</b> layer is persistent only when the container is stopped/started . If a continer is deleted, this data will be lost forever. Also this data is isolated only to that continer and cannot be shared with other containers.</p><p>So lets look at better data persistance models to share data between different containers on a Host.</p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-N4VAKoPiR1E/YKvEgBtXngI/AAAAAAAAH6o/439i5Hfd-z4MMzjtaiaN95NWWX6bcrqBQCLcBGAsYHQ/s1108/1.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="534" data-original-width="1108" height="308" src="https://1.bp.blogspot.com/-N4VAKoPiR1E/YKvEgBtXngI/AAAAAAAAH6o/439i5Hfd-z4MMzjtaiaN95NWWX6bcrqBQCLcBGAsYHQ/w640-h308/1.png" width="640" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><b><span style="color: #38761d;">Volume</span></b> and <span style="color: red;"><b>Bind Mount</b></span> are two ways of persistent data storage thats avilable on a Host, which can be accessed (read/write) by multiple containers.<p></p><p><b><span style="color: #38761d;">Volume</span></b> is the storage created and managed by Docker. This means no containers can go beyond the boundaries of docker while working with volumes.</p><p><span style="color: red;"><b>Bind Mount</b></span> is the storage directly from file system of the host file. So if there is a malicious code deployed in a container, it can break the host by manipulating the host filesystem.</p><p>By now looking at color coding you should have understood that Volume is a better/safer way of storing and sharing data between containers. Let me show you by a demonstration.</p><a name='more'></a><p>For below demonstration, i have Docker Desktop, Windows Subsystem for Linux 2.0, Ubuntu 18.04 LTS installed on my dev machine.</p><p>This will enable my windows machine to run a Linux machine which i will be using as a host.</p><p><b><span style="color: #38761d; font-size: large;"><i>Lets work with Volume first</i></span></b></p><p>Let me open the Linux server and check the docker version first.</p><p>Then i will create a volume and i will name it "<b>sharedvolume</b>". Even if there is no folders at this point, volume will create necessary folders inside docker space.</p><p>Then i will create a linux container named "<b>sender</b>" with the <b>sharedvolume</b> mounted as "/app" folder inside container</p><pre>docker --version
docker volume create sharedvolume
docker container run -dit --name sender --mount type=volume,source=sharedvolume,target=/app ubuntu</pre><p><a href="https://1.bp.blogspot.com/-OKFo4w8JAmI/YKuwTASSgxI/AAAAAAAAH5w/6KXMo0LNqhw_epHbA4ymBf2xthdIzSXQwCLcBGAsYHQ/s1226/2.png" style="margin-left: 1em; margin-right: 1em; text-align: center;"><img border="0" data-original-height="348" data-original-width="1226" height="182" src="https://1.bp.blogspot.com/-OKFo4w8JAmI/YKuwTASSgxI/AAAAAAAAH5w/6KXMo0LNqhw_epHbA4ymBf2xthdIzSXQwCLcBGAsYHQ/w640-h182/2.png" width="640" /></a></p>Lets list the running containers with <i>Docker Container ls</i> command.<div>Make a note of the CONTAINER ID of the sender container.</div><div><br /></div><div>Lets access the filesystem of the container and from inside the <b>sender</b> container i will create a text file with intent of storing some data in the volume(/app folder).</div><div><pre>docker attach <starting 4 letters of container id>
cd /app
echo "This is the data i want to share with receiver container">volumedata.txt
ls
more volumedata.txt
exit</pre></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-x4NNOctivoU/YKu0MHU7o6I/AAAAAAAAH54/7zuBjO8z1YQ3v0XJ9eDUvzbs9dD2XFmqwCLcBGAsYHQ/s1220/3.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="561" data-original-width="1220" height="294" src="https://1.bp.blogspot.com/-x4NNOctivoU/YKu0MHU7o6I/AAAAAAAAH54/7zuBjO8z1YQ3v0XJ9eDUvzbs9dD2XFmqwCLcBGAsYHQ/w640-h294/3.png" width="640" /></a></div><br /><div><i>more volumedata.txt</i> is a command to read the content of that file which is saved inside volume.</div><div><br /></div><div>Now lets create the second linux container named "<b>receiver</b>" with the same <b>sharedvolume</b> mounted as "/app" folder inside container. Then access the receiver container's file system, go to mounted <b>sharedvolume</b> (/app folder) and see if we can find volumedata.txt file and its content.</div><div><pre>docker container run -dit --name receiver --mount type=volume,source=sharedvolume,target=/app ubuntu
docker container ls
docker attach <first 4 letters of receiver container id>
cd /app
ls
more volumedata.txt</pre><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-MzuMrOGT8tw/YKu3W9UTOLI/AAAAAAAAH6A/bhwkGI1-tjQ3bQq-tS-him81-21VLpzCgCLcBGAsYHQ/s1215/4.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="404" data-original-width="1215" height="212" src="https://1.bp.blogspot.com/-MzuMrOGT8tw/YKu3W9UTOLI/AAAAAAAAH6A/bhwkGI1-tjQ3bQq-tS-him81-21VLpzCgCLcBGAsYHQ/w640-h212/4.png" width="640" /></a></div><br /><p>See the data saved from sender container was now accessible in receiver container. </p><p>Let me show the best part. If you open explorer on your windows dev machine and go to "\\wsl$\docker-desktop-data\version-pack-data\community\<b>docker</b>\<b>volumes</b>\sharedvolume\_data" path, you can see the volumedata.txt file here.</p><p>if you look at the path, you can clearly see that this data is created/stored insode the content of Docker.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-_JaSepffSS4/YKu4h3KcNlI/AAAAAAAAH6I/roN7pjYsA-0QPSLK4uaZcZUrSy3lE9twwCLcBGAsYHQ/s1675/5.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="294" data-original-width="1675" height="112" src="https://1.bp.blogspot.com/-_JaSepffSS4/YKu4h3KcNlI/AAAAAAAAH6I/roN7pjYsA-0QPSLK4uaZcZUrSy3lE9twwCLcBGAsYHQ/w640-h112/5.png" width="640" /></a></div><p><br /></p><p>Now lets stop both sender and receiver containers and delete them.</p><pre>docker container stop sender receiver
docker container rm sender receiver</pre><p><b></b></p><div class="separator" style="clear: both; text-align: center;"><b><a href="https://1.bp.blogspot.com/-hB9p9289kDY/YKu7PG9NfCI/AAAAAAAAH6Q/h9XsAQbqSkMqh3K45LGT14EVitNJ-DXJQCLcBGAsYHQ/s1227/6.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="277" data-original-width="1227" height="144" src="https://1.bp.blogspot.com/-hB9p9289kDY/YKu7PG9NfCI/AAAAAAAAH6Q/h9XsAQbqSkMqh3K45LGT14EVitNJ-DXJQCLcBGAsYHQ/w640-h144/6.png" width="640" /></a></b></div><b><span style="color: red;"><p><b><span style="color: red;"><br /></span></b></p><span style="font-size: large;"><i>Lets work with Bind Mount now.</i></span></span></b><p></p><p>Unlike Volume, you Bond Mount will not create folders, so you need to use an existing path/folder on your Linux host to store data.</p><p>Frist step is to create a folder called "bindstorage".</p><p>Then create the sender container again, but this time with bind-mount as the data storage mapped to /app folder.</p><p>The i will access sender container filesystem, create a binddata.txt with some content inside /app folder in the sender container.</p><pre>mkdir bindstorage
ls
docker container run -dit --name sender --mount type=bind,source="$(pwd)"/bindstorage,target=/app ubuntu
docker container ls
docker attach <first 4 letters of sender container>
cd app
echo "this is the data saved in bindmount">bindmountdata.txt
ls
more bindmountdata.txt
exit</pre><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-a-dq2AtNxCM/YKu_BeAcIVI/AAAAAAAAH6Y/afm6NWboOpgNcE-m5y6jzZDpZt2Npfd-ACLcBGAsYHQ/s1353/7.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="525" data-original-width="1353" height="248" src="https://1.bp.blogspot.com/-a-dq2AtNxCM/YKu_BeAcIVI/AAAAAAAAH6Y/afm6NWboOpgNcE-m5y6jzZDpZt2Npfd-ACLcBGAsYHQ/w640-h248/7.png" width="640" /></a></div><br /><p>Now lets create receiver container wiht same bind-mount storage to data sharing. </p><p>Then lets see if we can find bindmountdata.txt file and its content.</p><pre>docker container run -dit --name receiver --mount type=bind,source=$(pwd)/bindstorage,target=/app ubuntu
docker container ls
docker attach <first 4 letters of receiver container id>
cd app
ls
more bindmountdata.txt
exit</pre><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-NY7c8gZjmXM/YKvA7-Jj-rI/AAAAAAAAH6g/h6qx2Ql64gI-sLLvxtJPs4X5ns_mJEQFQCLcBGAsYHQ/s1355/8.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="383" data-original-width="1355" height="180" src="https://1.bp.blogspot.com/-NY7c8gZjmXM/YKvA7-Jj-rI/AAAAAAAAH6g/h6qx2Ql64gI-sLLvxtJPs4X5ns_mJEQFQCLcBGAsYHQ/w640-h180/8.png" width="640" /></a></div><br /><p>The data saved from sender container can now be read in receiver container. </p><p><span style="font-size: large;"><i>You may have a question "apart from some syntax changes, both seems like same? why is volume is preffered one?"</i></span></p><p>Let me show the worst part of Bind-Mount storage model.</p><p>If you open the file explorer on your windows dev machine, and go to "\\wsl$\Ubuntu-18.04\home\linuxusr\bindstorage" path, you can see the bindmountdata.txt file.</p><p>This means the storage used in on the file system of the Hosting machine and with a bit of tweaking, any malicious code in a container can mess-up the Linux host and can break all other containers on that host, even scan for the details of other images and containers on the host.</p><p>Check this <a href="https://blog.aquasec.com/dirty-cow-vulnerability-impact-on-containers" target="_blank">dirtyCOW</a> which is a famous container vulnerability back in the day.</p><p>Hope i am helpful to some fellow developers.</p><p></p></div>Pratap Reddy Pilakahttp://www.blogger.com/profile/17141976956878163883noreply@blogger.com1tag:blogger.com,1999:blog-4208378482021219922.post-56666114089275699062021-05-22T21:39:00.000-07:002021-05-22T21:39:51.778-07:00Containerize ASP.Net Core app on Azure Kubernetes Cluster<p>In my <a href="https://pratapreddypilaka.blogspot.com/2021/05/containerizing-aspnet-core-application.html" target="_blank">earlier post</a>, we have deployed ASP.Net Core application to a Container hosted by a Linux Server.</p><p><b>There are some problems with this approach.</b></p><p></p><ol style="text-align: left;"><li>What if the Host VM is stopped?</li><li>What if Container Instance is stopped?</li><li>How do we manage the deployment of any app changes?</li><li>Even when we stop Host VM, you still be paying for the Disk allocated. How we can avoid that?</li></ol><p></p><p>This is where Azure Kubernetes comes into the picture. </p><p>Azure Kubernetes provide serverless CI/CD experience which also manages Health, Security, Auto-Scaling, Deployment and Governance aspects. More details can be <a href="https://azure.microsoft.com/en-us/services/kubernetes-service/" target="_blank">found here</a>. </p><p><b>In this article we will be:</b></p><p></p><ul style="text-align: left;"><li>Deploy a ASP.Net Core App to a Container</li><li>Create a Container Image from .Net Core Container</li><li>Push the Image to Azure Container Registry</li><li>Use Kubernetes to Pull that image and create mutliple instances of the container in Kubernete Pods.</li><li>Expose the .Net Core App via Azure Load-Balancer.</li></ul><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-4P5P1L3IB2Y/YKnEdHEEPMI/AAAAAAAAH3w/54lgtYJfDh8j_beTLZo5v8Nh-bC5Ph-bQCLcBGAsYHQ/s1044/4.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="617" data-original-width="1044" height="378" src="https://1.bp.blogspot.com/-4P5P1L3IB2Y/YKnEdHEEPMI/AAAAAAAAH3w/54lgtYJfDh8j_beTLZo5v8Nh-bC5Ph-bQCLcBGAsYHQ/w640-h378/4.png" width="640" /></a></div><a name='more'></a><br /><div><b>Step 1: Create a ASP.Net Core Web App. </b></div><div>I named it "<b>DemoContainerApp</b>". </div><div>Since we need to containerize and generate an image of that container with out provisioning a Host, you need to select Docker support while selecting additional properties.</div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-NzF8FsujzcI/YKnGJi9PdmI/AAAAAAAAH34/_ROQP-H_TA0U3fg3jEdVd28Pd3pS_HKZQCLcBGAsYHQ/s987/5.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="679" data-original-width="987" height="275" src="https://1.bp.blogspot.com/-NzF8FsujzcI/YKnGJi9PdmI/AAAAAAAAH34/_ROQP-H_TA0U3fg3jEdVd28Pd3pS_HKZQCLcBGAsYHQ/w400-h275/5.png" width="400" /></a></div><br /><div>This may ask you to download "Docker Desktop" and "WSL 2.0 for Linux". Do it.</div><div>Now you have your ASP.Net Core App created and try running on IIS Express.</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-HdIHkbqMoco/YKnHCsR5g7I/AAAAAAAAH4A/u7reT9IbLGgkgucyjjhjyoR7IEQgEPuTQCLcBGAsYHQ/s1310/6.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="470" data-original-width="1310" height="230" src="https://1.bp.blogspot.com/-HdIHkbqMoco/YKnHCsR5g7I/AAAAAAAAH4A/u7reT9IbLGgkgucyjjhjyoR7IEQgEPuTQCLcBGAsYHQ/w640-h230/6.png" width="640" /></a></div><b><p><b><br /></b></p>Step 2: Creating Container Registry</b><br /><div>Go to Azure Portal, start by creating a Resource group for Container Registry. I named it "ContainerRegistryRG". Now search for "Container Registry" and create new one.</div><div>I have created a Container Registry named "MyAppRegistry247".</div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://1.bp.blogspot.com/-V5gSdV31lWA/YKnIsn48apI/AAAAAAAAH4I/BUoCXYT61pACVG78Wb6heb9e0W2n72aJQCLcBGAsYHQ/s1396/7.png" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="501" data-original-width="1396" height="230" src="https://1.bp.blogspot.com/-V5gSdV31lWA/YKnIsn48apI/AAAAAAAAH4I/BUoCXYT61pACVG78Wb6heb9e0W2n72aJQCLcBGAsYHQ/w640-h230/7.png" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;"></td></tr></tbody></table><b><br /></b><p></p><p><b>Step 3: Deploy Container Image to Azure Container Registry</b><br /></p><div>We will use the capabilities of Visual studio combined with Docker Desktop to create a Container hosting our App then extract an image of of that container and push it to ACR.</div><div><br /></div><div>Now right click on the solution and select Publish, and select the Azure Container Registry you created in Step 2.</div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-3-RpLaXHP6Q/YKnK-X_tM6I/AAAAAAAAH4Q/WrP0phXJq-4GmKzUEmkUN0ZaG0prKxXMgCLcBGAsYHQ/s1193/8.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1086" data-original-width="1193" height="582" src="https://1.bp.blogspot.com/-3-RpLaXHP6Q/YKnK-X_tM6I/AAAAAAAAH4Q/WrP0phXJq-4GmKzUEmkUN0ZaG0prKxXMgCLcBGAsYHQ/w640-h582/8.png" width="640" /></a></div>Now publish the Application. This will do the following:<p></p><p></p><ul style="text-align: left;"><li>Deploy the current .Net Core Web App, to a Docker Container Using Docker Desktop.</li><li>Extract the Image of that said container and push it to Azure Container Registry</li></ul><p></p><p>To verify this, open the Docker Desktop and click on Images and you will see bot of the images.<br /></p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-Pu1pYWgA5Cs/YKnMpvwWudI/AAAAAAAAH4Y/jZ7vib2MPGAWrIF_WFVwM6CDU2JJqTe0QCLcBGAsYHQ/s1667/9.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="614" data-original-width="1667" height="236" src="https://1.bp.blogspot.com/-Pu1pYWgA5Cs/YKnMpvwWudI/AAAAAAAAH4Y/jZ7vib2MPGAWrIF_WFVwM6CDU2JJqTe0QCLcBGAsYHQ/w640-h236/9.png" width="640" /></a></div><br /><div>Now open Azure portal and search for Container Registry and open "<b>MyAppRegistry247</b>". Go to "<b>Respositories</b>", you should see the container image for democontainerapp.</div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-F2DqqEADqdI/YKnNWIFxySI/AAAAAAAAH4g/gP_41W-YSWQy6aKIpJFvpif7gfWk6jfcACLcBGAsYHQ/s1765/10.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="947" data-original-width="1765" height="344" src="https://1.bp.blogspot.com/-F2DqqEADqdI/YKnNWIFxySI/AAAAAAAAH4g/gP_41W-YSWQy6aKIpJFvpif7gfWk6jfcACLcBGAsYHQ/w640-h344/10.png" width="640" /></a></div><br /><div><br /></div><div><b>Step 4: Create Azure Kubernetes Service Cluster</b></div><div>Lets start by creating Resource Group for AKS. I named it "aksRG".</div><div>Before you create Kubernetes cluster , you need to understand about <b>Service Principal</b>.</div><div><br /></div><div>To simply put, <b>Service Principal</b> is the AAD credentials used by Kubernetes to interact with Container Registry to pull the container image to create instances of it in kubernete pods.</div><div><br /></div><div>If you havent dealth with next step properly, you may end up with some errors like below.</div><div><br /></div><div><span style="background-color: #fcff01;"><span face="-apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"" style="color: #24292e; font-size: 14px;"><i>"Cannot find user or service principal in graph database for 'msi'. If the assignee is an appId, make sure the corresponding service principal is created with 'az ad sp create --id msi'"</i></span> </span></div><div><br /></div><div>Execute below code to create a new Service Principal that can be used for Kubernetes interaction with ACR.</div><div><pre>az ad sp create-for-rbac --skip-assignment --name demoAKSClusterServicePrincipal</pre></div><div>This will generate Service principal with all the details displayed like below. </div><div>Copy<b> appId</b> and <b>password </b>(client secret) for creating Kubernetes Cluster. </div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-dNlyxwoYpak/YKnQ2rGvLOI/AAAAAAAAH4o/5TIw3MYFZvkLE4qMHMVVPZQiPczsZ0zngCLcBGAsYHQ/s1354/3.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="331" data-original-width="1354" height="156" src="https://1.bp.blogspot.com/-dNlyxwoYpak/YKnQ2rGvLOI/AAAAAAAAH4o/5TIw3MYFZvkLE4qMHMVVPZQiPczsZ0zngCLcBGAsYHQ/w640-h156/3.png" width="640" /></a></div><br /><div>Execute below commands</div><div><pre>#Creating Kubernetes Cluster with Service principal association
az aks create --resource-group aksRG --name <span style="background-color: #fcff01;">demo-aks-cluster</span> --node-count 1 --enable-addons monitoring --service-principal <appid> --client-secret <password>
#Install Kubernetes command tools
az aks install-cli --install-location=./kubectl
#Update the Kubernetes config with the service principal details
az aks get-credentials --resource-group aksRG --name demo-aks-cluster </password></appid></pre></div><div>With above commands AKS Cluster is ready, but nothing has been deployed yet.</div><div><br /></div><div><b>Step 5: Run Kubectl commands to create Container intances load balanced.</b></div><div>There are two parts to hosting containers on Kubernetes pods. <b>App</b> & <b>Service</b></div><div>If you open AKS Cluster we created in step 4, you will see options "WorkLoad" and "Services" in left pane.</div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-zqPznAHWdcs/YKnUes52mEI/AAAAAAAAH4w/lwaEubhiRLQV_TsutvegcNIW3lMACQOgACLcBGAsYHQ/s406/11.png" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="253" data-original-width="406" src="https://1.bp.blogspot.com/-zqPznAHWdcs/YKnUes52mEI/AAAAAAAAH4w/lwaEubhiRLQV_TsutvegcNIW3lMACQOgACLcBGAsYHQ/s320/11.png" width="320" /></a></div><br /><div><br /></div><div><b>Workloads</b> is defined by <b>app.yml </b>file which will have details of container image details and number of Pods you want to host the containers.</div><div><br /></div><div><b>Services and Ingresses</b> is defined by <b>service.yml</b> file have details of which workload should be hosted on what port.</div><div><br /></div><div><br /></div><div><br /></div><div>Create 2 files app.yml and service.yml with below content.</div><div><br /></div><div><u><b>app.yml Content</b></u></div><div><pre>apiVersion: apps/v1
kind: Deployment
metadata:
name: democontainerapp-deployment
spec:
replicas: 2
selector:
matchLabels:
app: democontainerapp
template:
metadata:
labels:
app: democontainerapp
spec:
containers:
- name: democontainerapp
image: myappregistry247.azurecr.io/democontainerapp
ports:
- containerPort: 5000</pre></div><div><b><u>service.yml Content</u></b></div><div><pre>apiVersion: v1
kind: Service
metadata:
name: democontainerapp-service
spec:
type: LoadBalancer
ports:
- port: 80
selector:
app: democontainerapp</pre>Open Azure Portal, open Azure Cloud Shell.</div><div>Upload both the app.yml and service.yml files.</div><div>Run below command to get the ClientID from the service principal associated to Kubernetes Cluster and create a Role based access to Container Registry so that container images can be accessed by AKS.</div><div><pre>$AKS_RESOURCE_GROUP="aksRG"
$AKS_CLUSTER_NAME="demo-aks-cluster"
$ACR_RESOURCE_GROUP="ContainerRegistryRG"
$ACR_NAME="myappregistry247"
$CLIENT_ID=$(az aks show --resource-group $AKS_RESOURCE_GROUP --name $AKS_CLUSTER_NAME --query "servicePrincipalProfile.clientId" --output tsv)
$ACR_ID=$(az acr show --name $ACR_NAME --resource-group $ACR_RESOURCE_GROUP --query "id" --output tsv)
az role assignment create --assignee $CLIENT_ID --role acrpull --scope $ACR_ID</pre></div><div>Now run below kubectl commands to create both workloads and services using app.yml and service.yml file we uploaded earlier.</div><div><pre>#Create workloads
kubectl apply -f app.yml
#Create service and ingress rules with ports and loadbalancers
kubectl apply -f service.yml
#Bring the External IP address of load balancers for the pods hosting containers
kubectl get service democontainerapp-service --watch</pre></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-Yh8ADynAyN0/YKnZ5_g1-rI/AAAAAAAAH44/6SXsJzLiFQoXI1CAQSJtFEWjVSNKEDk-gCLcBGAsYHQ/s1226/13.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="337" data-original-width="1226" height="176" src="https://1.bp.blogspot.com/-Yh8ADynAyN0/YKnZ5_g1-rI/AAAAAAAAH44/6SXsJzLiFQoXI1CAQSJtFEWjVSNKEDk-gCLcBGAsYHQ/w640-h176/13.png" width="640" /></a></div><br /><div>Now go to said workloads and Services and Ingresses in Kubernetes Cluster and see them for yourself.</div><div><b><u>Workloads</u></b></div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-KMPVVjG03Gs/YKnagLePgOI/AAAAAAAAH5A/9NIWHHdvyAMWAgyXlO7rBw-12WteAycSQCLcBGAsYHQ/s2175/14.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="404" data-original-width="2175" height="118" src="https://1.bp.blogspot.com/-KMPVVjG03Gs/YKnagLePgOI/AAAAAAAAH5A/9NIWHHdvyAMWAgyXlO7rBw-12WteAycSQCLcBGAsYHQ/w640-h118/14.png" width="640" /></a></div><br /><div><b><u>Services and Ingresses</u></b></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-SfzgxkwBXf4/YKnaozfJy-I/AAAAAAAAH5E/ehFJ3NRyP_cfrVyWu899vCN-aClNeZnoACLcBGAsYHQ/s2180/15.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="408" data-original-width="2180" height="120" src="https://1.bp.blogspot.com/-SfzgxkwBXf4/YKnaozfJy-I/AAAAAAAAH5E/ehFJ3NRyP_cfrVyWu899vCN-aClNeZnoACLcBGAsYHQ/w640-h120/15.png" width="640" /></a></div><br /><div>Copy the External IP with port in a browser and now you can access ASP.Net core hosted in a container on Kubernetes Cluster.</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-GAG5BkVLOrY/YKnbP0sTv9I/AAAAAAAAH5Q/h5Hn-WYYlUA63IxDFR2Fy9d22Zx1OI_iQCLcBGAsYHQ/s1972/16.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="427" data-original-width="1972" height="138" src="https://1.bp.blogspot.com/-GAG5BkVLOrY/YKnbP0sTv9I/AAAAAAAAH5Q/h5Hn-WYYlUA63IxDFR2Fy9d22Zx1OI_iQCLcBGAsYHQ/w640-h138/16.png" width="640" /></a></div><br /><div>In next article, we will automate the code deployment and Rolling deployments in Kubernetes using Azure DevOps Pipeline.</div><div><br /></div><div>Hope i am helpful to fellow developers.</div><p></p>Pratap Reddy Pilakahttp://www.blogger.com/profile/17141976956878163883noreply@blogger.com0tag:blogger.com,1999:blog-4208378482021219922.post-28211476333096262962021-05-09T21:04:00.002-07:002021-05-22T03:59:59.986-07:00Containerizing ASP.Net Core Application on a Linux HostIn this article we will see how to deploy a ASP.Net Core 3.1 Webapplication to a container hosted on a Linux Host.<div>Beofore we jump into implementation, we need to look at every component and know what it does.</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-pG-2-khu42A/YKjkHxHLAwI/AAAAAAAAH3k/AVGg6aFZlyI6Lak6P2BOXJsO24OmrS--gCLcBGAsYHQ/s1177/1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="615" data-original-width="1177" height="334" src="https://1.bp.blogspot.com/-pG-2-khu42A/YKjkHxHLAwI/AAAAAAAAH3k/AVGg6aFZlyI6Lak6P2BOXJsO24OmrS--gCLcBGAsYHQ/w640-h334/1.png" width="640" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div>1. <b>Host</b>: This can be a Linux or Windows server. Considering Linux servers have used from long time for contanerization, i picked it. But Windows is very close choice considering all the capabilities it acquired in last 2 years.</div><div><br /></div><div>2. <b>Docker:</b> It is the engine we use to host our containers. It will package the application with its dependecies and run them in isolated containers on the host.</div><div><br /></div><div>3 <b>Reverse Proxy Server</b>: This will be sitting behind the firewall and redirects the user requests to appropriate apps/containers. This will provide extra layer of abstraction and reduces the public exposure of the containers. </div><div><br /></div><div>4. <b>Kestrel Web Server</b>: When you install a .Net Core SDK, it will internally create a webserver to act as a backend server for the .Net Application. Proxy server will be sending the client calls to Kestrel Web server and responds back with the output from .Net App.</div><div><br /></div><div>5. <b>App</b>: .Net Core Application we deploy to be containerized. </div><a name='more'></a><div>Lets begin the implemntation.</div><div><br /></div><div><b>Step1: Provisioning the Host</b></div><div>Its recommended that you start using Azure Key Vault for storing the Admin credentials, SSH keys and</div><div>Certficate Private keys. So i have my Linux Admin password in Key valut.</div><div>I am creating a Linux server using below CLI commands.</div><div><pre>$psw=az keyvault secret show --vault-name AdminCreds -n MyLinuxAdminPSW --query value
az vm create -g LinContainerRG -n LinuxHost --image UbuntuLTS --admin-username linuxadminuser --admin-password $psw</pre></div><div>Once the Host is created, go to the virtual machine in zure portal, go to Networking and open Port 80 for TCP connections.</div><div><br /></div><div><b>Step 2: Install Docker on the Host</b></div><div>For this we need Putty to connect to our linux host. Download it from <a href="https://www.putty.org/" target="_blank">this link</a>, and install.</div><div>Go to Azure portal and copy the Public IP of the host. Open a new session connection to the host using Putty. You need Public IP of the host for this. </div><div>Once you try to open the session, it will prompt you for credentials, use the admin credentials for now.</div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-bHPmM3oO_mA/YJiLhFPbLBI/AAAAAAAAH1Q/niWG2heCof4c9HgSKRK8Kv1pCoh1HkzIwCLcBGAsYHQ/s864/2.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="634" data-original-width="864" height="294" src="https://1.bp.blogspot.com/-bHPmM3oO_mA/YJiLhFPbLBI/AAAAAAAAH1Q/niWG2heCof4c9HgSKRK8Kv1pCoh1HkzIwCLcBGAsYHQ/w400-h294/2.png" width="400" /></a></div><br /><div>Run below commands one by one to install Docker engine on your host.</div><div><pre>sudo apt update
sudo apt install apt-transport-https ca-certificates curl software-properties-common
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu bionic stable"
sudo apt update
sudo apt install docker-ce</pre></div><div></div><div>Now we got docker installed on our host. </div><div><br /></div><div><b>Step 3: Setup Reverse Proxy Server</b></div><div>A proxy server is a machine with its own IP, which makes web requests on behalf of other machines/users. These can provide badwidth, privacy and additional layer of sceurity.</div><div><br /></div><div>There are two types of proxy servers, below picture speaks volumnes about it.</div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-pc0zSahP0e4/YJiPpbgutBI/AAAAAAAAH1g/a5HEdQBJrkA-l7UCmf-kGXw-bLkUdj7MACLcBGAsYHQ/s1323/3.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="794" data-original-width="1323" height="240" src="https://1.bp.blogspot.com/-pc0zSahP0e4/YJiPpbgutBI/AAAAAAAAH1g/a5HEdQBJrkA-l7UCmf-kGXw-bLkUdj7MACLcBGAsYHQ/w400-h240/3.png" width="400" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div>Now you know whay we are using Reverse Proxy server infront of our containers.</div><div>You can use IIS Express, Apache or NGINX as a proxy server. </div><div>We will setup the proxy server on Host, by executing below commands. </div><div><pre>sudo docker pull nginx:1.17.0
sudo docker images
sudo apt-get install nginx
sudo apt update</pre></div><div>At this point if you try to broswe for http://PublicIP of your LinuxHost, this is what you see.</div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-H8T-LnzpzIg/YJitr_xpHTI/AAAAAAAAH2Q/m1dx8eESGpM8clj5FMJxyYnuUjvXxnOxwCLcBGAsYHQ/s1352/9.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="461" data-original-width="1352" height="218" src="https://1.bp.blogspot.com/-H8T-LnzpzIg/YJitr_xpHTI/AAAAAAAAH2Q/m1dx8eESGpM8clj5FMJxyYnuUjvXxnOxwCLcBGAsYHQ/w640-h218/9.png" width="640" /></a></div><br /><div><b>Step 4: Create .Net Core Application</b></div><div>We are now creating a basic Asp.Net Core webapplication. Go to Visual Studio 2019 and create a "ASP.Net Core Web Application". Framework as .Net Core 3.1</div><div>I call it "DemoCoreApp".</div><div>There are 2 small changes you need to do in code.</div><div><br /></div><div><b>1.</b> Go to Startup.cs and replace this line "app.UseHttpsRedirection();" with below code.</div><div><pre>app.UseForwardedHeaders(new ForwardedHeadersOptions
{
ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
});</pre></div><div>As our core app will not be receiing call in ragulat client server fasion (http), it has to use Forward headers to identify protocal and IP details of the client. The above lines of code will take care of it.</div><div><br /></div><div><b>2.</b> Go to solution and add a file with name "Dockerfile" without any extention. Add below code to it.</div><div><pre>FROM mcr.microsoft.com/dotnet/core/sdk:3.1
WORKDIR /app
COPY . .
ENV ASPNETCORE_URLS http://*:5000
EXPOSE 5000
ENTRYPOINT ["dotnet", "DemoCoreApp.dll"]</pre></div><div>I will explain every line of this code in following step. </div><div>Right click on the Docketfile and go to properties, chnage Copy to Output Directory to "Copy always".</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-3nAYNgOHB0A/YJiavL7pGtI/AAAAAAAAH1o/Nh1_kbEEiTAoWk1pXVW7NgbZwvD1T9kBwCLcBGAsYHQ/s530/4.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="348" data-original-width="530" height="263" src="https://1.bp.blogspot.com/-3nAYNgOHB0A/YJiavL7pGtI/AAAAAAAAH1o/Nh1_kbEEiTAoWk1pXVW7NgbZwvD1T9kBwCLcBGAsYHQ/w400-h263/4.png" width="400" /></a></div><div><br /></div>Now build the project and Publish to a Folder, the published content will be in "\bin\Release\netcoreapp3.1/Publish" folder. Check that Dockerfile is copied to Publish folder.<div><br /></div><div><b>Step 5: Transfer the Published content o Host</b></div><div>Download "WinSCP" from this <a href="https://winscp.net/eng/download.php" target="_blank">link</a>. Install it and open WinSCP to transfer publish folder to Host.</div><div>Connect the Host using the PublicIP address and the user credentials.</div><div>Left side shows the local files and right side shows the Azure host's file system.</div><div>Navigate to "\bin\Release\netcoreapp3.1/" on left side and make sure you opened "/home/Username" directory on your host.</div><div>Now Drag and drop the publish folder.</div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-G5uRVVp45NE/YJieJeRryzI/AAAAAAAAH1w/i4iuegE_WYotMQd0l4ck1xIH9RavzV0QACLcBGAsYHQ/s1507/5.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="766" data-original-width="1507" height="326" src="https://1.bp.blogspot.com/-G5uRVVp45NE/YJieJeRryzI/AAAAAAAAH1w/i4iuegE_WYotMQd0l4ck1xIH9RavzV0QACLcBGAsYHQ/w640-h326/5.png" width="640" /></a></div><br /><div>Now go back to your Putty session, and run bwlow code.</div><div><pre>cd /etc/nginx/sites-available
sudo chmod 667 default</pre></div><div>This will grant you edit access to dafult NGINX routing file.</div><div>Remeber we taked about reverse proxy where the proxy server routes the calls to containers based on certain conditions. Now we are going to write those conditions in that default file.</div><div><br /></div><div>Open WinSCP tool and now navigate to /etc/nginx/sites-available/ and edit the default file.</div><div>Paste below content replacing the line "try_files $uri $uri/ =404;"</div><div><pre>proxy_pass http://localhost:5000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection keep-alive;
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;</pre></div><div>This will take care of forwarding our calls to the .Net Core container.</div><div>Save and close it.</div><div><br /></div><div>Now go back to Putty session and run below command.</div><div><pre>sudo nginx -s reload</pre></div><div></div><div>In some cases you might get an error while reloading NGINX, saying </div><div><b><i>nginx: [error] open() "/run/nginx.pid" failed (2: No such file or directory)</i></b></div><div><br /></div><div>There are many fixes on google for this which suites for PROD environment, where you cannot afford to restart. But here i can. So lets go to Azure portal and restart the Linux Host.</div><div><br /></div><div>Now connect to Host using Putty and run above command.</div><div><br /></div><div><b>Step 6: Setting up container for .Netcore App</b></div><div>As i promised earlier, lets have a look at the commands in docker file which we embedded in app.</div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-uFUIcF7tiWI/YJiq3yl4PII/AAAAAAAAH14/FnLMV9OC9z8_oY9Zs7bnfLirW7piU2gFACLcBGAsYHQ/s1391/7.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="493" data-original-width="1391" height="226" src="https://1.bp.blogspot.com/-uFUIcF7tiWI/YJiq3yl4PII/AAAAAAAAH14/FnLMV9OC9z8_oY9Zs7bnfLirW7piU2gFACLcBGAsYHQ/w640-h226/7.png" width="640" /></a></div><div><br /></div>Now go to Putty session and run below commands one after another.</div><div><pre>cd publish
sudo docker build -t democoreapp .
sudo docker run -d -p 5000:5000 democoreapp</pre>Above command will execute the content from Dockerfile in publish folder and create a container to host .Net Core App on port 5000 in kestrel webserver.</div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-49CJHpVQ8Mo/YJisXKGXPVI/AAAAAAAAH2A/RUGISrOn2z0AT4xOZV54yAuFfGZQlBFAACLcBGAsYHQ/s1013/8.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="865" data-original-width="1013" height="546" src="https://1.bp.blogspot.com/-49CJHpVQ8Mo/YJisXKGXPVI/AAAAAAAAH2A/RUGISrOn2z0AT4xOZV54yAuFfGZQlBFAACLcBGAsYHQ/w640-h546/8.png" width="640" /></a></div><div><br /></div>Now on your dev machine, if you browse for http://PublicIP of your Linux Host, this is what you see.<br /><div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-u8YnegUJCi0/YJitQfZlmfI/AAAAAAAAH2I/S7aQAdMJWKg9aSIcEp27wBygimMCgcDggCLcBGAsYHQ/s1391/10.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="430" data-original-width="1391" height="198" src="https://1.bp.blogspot.com/-u8YnegUJCi0/YJitQfZlmfI/AAAAAAAAH2I/S7aQAdMJWKg9aSIcEp27wBygimMCgcDggCLcBGAsYHQ/w640-h198/10.png" width="640" /></a></div><br /><div>Now, how do we know that app is running from container and not from Host itself?</div><div>Run below command to see list of containers running.</div><div><pre>sudo docker ps</pre></div><div><b><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-bk-ay0OkrmM/YJivHSiWqrI/AAAAAAAAH2Y/cDW71AMSCognvzZ6ivvg1LwLOGaAK-eLwCLcBGAsYHQ/s1759/11.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="229" data-original-width="1759" height="84" src="https://1.bp.blogspot.com/-bk-ay0OkrmM/YJivHSiWqrI/AAAAAAAAH2Y/cDW71AMSCognvzZ6ivvg1LwLOGaAK-eLwCLcBGAsYHQ/w640-h84/11.png" width="640" /></a></div><br /></b></div><div>This will show all the containers running, and we can stop the container by running below command.</div><div><pre>sudo docker stop XXX</pre></div><div>XXX being the first 3 digits of CONTAINER ID, highlighted in above screenshot.</div><div>Now the container is stopped , and try accessing the app now.</div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-Mj-YR8395JM/YJivlGhJs-I/AAAAAAAAH2g/mFH3ZAmyzPEWbEAma5l8TzXMSKR4Nw3IACLcBGAsYHQ/s1270/12.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="389" data-original-width="1270" height="196" src="https://1.bp.blogspot.com/-Mj-YR8395JM/YJivlGhJs-I/AAAAAAAAH2g/mFH3ZAmyzPEWbEAma5l8TzXMSKR4Nw3IACLcBGAsYHQ/w640-h196/12.png" width="640" /></a></div>Now we know that App is hosted in its own container on a Linux Host.</div><div>Lets start it back, </div><div><div><pre>sudo docker run -d -p 5000:5000 democoreapp</pre></div><div>Its back online.</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-bX3O07bHxYg/YJjQGB51QiI/AAAAAAAAH24/L7Bo4vpIAC8DniDo5XsQS9VhuPdBjolUwCLcBGAsYHQ/s1160/1.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="601" data-original-width="1160" height="332" src="https://1.bp.blogspot.com/-bX3O07bHxYg/YJjQGB51QiI/AAAAAAAAH24/L7Bo4vpIAC8DniDo5XsQS9VhuPdBjolUwCLcBGAsYHQ/w640-h332/1.png" width="640" /></a></div></div><br />We have sucesfully deployed .Net Core App in a container on a Linux HostPratap Reddy Pilakahttp://www.blogger.com/profile/17141976956878163883noreply@blogger.com0tag:blogger.com,1999:blog-4208378482021219922.post-23292874464594216692021-05-05T18:57:00.004-07:002021-05-05T18:57:44.922-07:00Azure Service Bus - Queues and Topics - Part 2<p><b>Use Case:</b> Organization X has a large-scale distributed serverless application environment. There are some azure components that sends a private messages which need to be accessed only once by other components using RoleBasedAccessControls. There are some messages that need to Fan-Out(one-to-many) to large number of systems, where sender doesnt need to know the receiver's details. The message should stay until time out, for receiving components to consume when they are avilable.</p><p>In the <b><span style="color: red;"><a href="https://pratapreddypilaka.blogspot.com/2021/05/azure-service-bus-queues-and-topics.html" target="_blank">earlier article</a></span></b>, we have seen how we can use Azure Service Bus Queues for integration of serverless application environment. Now we will explore Service Bus Topic and how it will be used.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-8TBmVC5Hz0E/YJMi0QbQIgI/AAAAAAAAH0A/c7zluzHGTswAM4nD701rHwGp5D9my5WRQCLcBGAsYHQ/s1179/12.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="677" data-original-width="1179" height="368" src="https://1.bp.blogspot.com/-8TBmVC5Hz0E/YJMi0QbQIgI/AAAAAAAAH0A/c7zluzHGTswAM4nD701rHwGp5D9my5WRQCLcBGAsYHQ/w640-h368/12.png" width="640" /></a></div><br /><p>We have already created Azure service Bus in earlier article, you can reffer the link provoided at the top for details.</p><a name='more'></a><p><b>Step 1: Creating Azure Service Bus Topic</b></p><p><span style="color: #222222;">Go to azure portal, and open the Service Bus we created "DemoAZSvcBus". </span><span style="color: #222222;">In left pannel under "Entities", click "Topics".</span></p><div><span style="color: #222222;">Create a new Topic, name it "CorpAnnouncements".</span></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-RLE8mHsN_b8/YJLBaYRPSII/AAAAAAAAHxE/Rfsn_AaQ3fYiBnQj6GIW2mfpRBxYaozuQCLcBGAsYHQ/s647/4.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="612" data-original-width="647" height="379" src="https://1.bp.blogspot.com/-RLE8mHsN_b8/YJLBaYRPSII/AAAAAAAAHxE/Rfsn_AaQ3fYiBnQj6GIW2mfpRBxYaozuQCLcBGAsYHQ/w400-h379/4.png" width="400" /></a></div>Once Topic is created, Open it. Now in left pannel, under "Entities", click on "Subscriptions".</div><div>Create a new Subscription , name it "NZSubscription".</div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-MnF987elwcU/YJLDT0uVU7I/AAAAAAAAHxM/dxo9kseHbl0HxzlRSm8gCCJsOfxLfo_9ACLcBGAsYHQ/s889/5.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="523" data-original-width="889" height="235" src="https://1.bp.blogspot.com/-MnF987elwcU/YJLDT0uVU7I/AAAAAAAAHxM/dxo9kseHbl0HxzlRSm8gCCJsOfxLfo_9ACLcBGAsYHQ/w400-h235/5.png" width="400" /></a></div><div><div><b>Step 2: Get Connection string for Azure Service Bus Namespace.</b></div><div>It costs money to create a storage account in azure, especially when you are using free tier or experimenting on your own. Thats why i use Azure CLI on Powershell 7.</div><div><br /></div><div>Open Power shell and run below commands. After first command, you may need to authenticate to Azure console via browser, then close the browser. Make sure you check update Resource group name and Service Bus name in your command.</div><div><pre>az login <br />az servicebus namespace authorization-rule keys list --resource-group <span style="background-color: #fcff01;">DefaultRG</span> --name RootManageSharedAccessKey --query primaryConnectionString --output tsv --namespace-name <span style="background-color: #fcff01;">DemoAZSvcBus</span></pre></div><div>This command will bring back the connection string which you need to keep it aside for following steps. It will be in this formatt.</div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-snOPU_2kcNA/YJLG44xAOnI/AAAAAAAAHxc/GzB2DhjKo8k7VKwq41PP4FpbkC48sgzpQCLcBGAsYHQ/s1344/6.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="220" data-original-width="1344" height="104" src="https://1.bp.blogspot.com/-snOPU_2kcNA/YJLG44xAOnI/AAAAAAAAHxc/GzB2DhjKo8k7VKwq41PP4FpbkC48sgzpQCLcBGAsYHQ/w640-h104/6.png" width="640" /></a></div><br /><div><b>Step 3: Create CorpMessageSender (Service Bus Topic Message Sender)</b></div></div><div><div>Lets start with a folder "ServiceBus". All the projects from here on will be created inside it.</div><div>Open Shell, navigate to "ServiceBus" folder. Run below commands one after another.</div><div><br /></div><div><pre>dotnet new console -n CorpAnnouncementSender<br />CD .\CorpAnnouncementSender\<br />dotnet add package Microsoft.Azure.ServiceBus<br />Code .</pre></div></div><div><br /></div><div><div>These command will create a new .Net Console application, then install Azure Service Bus package and open the project in VS Code.</div><div><br /></div><div>Copy below code to program.cs. You may need to write similar code in your azure component which is sending out secured, transcation bound messages to other systems.</div><div><br /></div><div><pre>using System;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Azure.ServiceBus;
namespace CorpAnnouncementSender
{
class Program
{
const string ServiceBusConnectionString = "<span style="background-color: #fcff01;">Service Bus Connecttion string goes here</span>";
const string TopicName = "corpannouncements";
static ITopicClient topicClient;
static void Main(string[] args)
{
Console.WriteLine("Sending a message to the corporate annpuncements topic...");
SendCorpAnnouncementAsync().GetAwaiter().GetResult();
Console.WriteLine("Message was sent successfully.");
}
static async Task SendCorpAnnouncementAsync()
{
topicClient = new TopicClient(ServiceBusConnectionString, TopicName);
try
{
string messageBody = $"Organization X going public IPO in Jan 2022";
var message = new Message(Encoding.UTF8.GetBytes(messageBody));
Console.WriteLine($"Sending message: {messageBody}");
await topicClient.SendAsync(message);
}
catch (Exception exception)
{
Console.WriteLine($"{DateTime.Now} :: Exception: {exception.Message}");
}
await topicClient.CloseAsync();
}
}
}</pre></div><div><div>Save program.cs file, open shell and run </div><div><pre>dotnet build</pre></div><div>and now navigate to "ServiceBus" folder, run below command.</div><div><pre>dotnet run -p CorpAnnouncementSender</pre></div><div>You will see the result like this.</div></div></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-Lf_Ap3U8gsg/YJMqMjYh0mI/AAAAAAAAH0I/llfLsABFC4k1vpiqn0_fR7BJF_4WYIyFQCLcBGAsYHQ/s1155/2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="549" data-original-width="1155" height="304" src="https://1.bp.blogspot.com/-Lf_Ap3U8gsg/YJMqMjYh0mI/AAAAAAAAH0I/llfLsABFC4k1vpiqn0_fR7BJF_4WYIyFQCLcBGAsYHQ/w640-h304/2.png" width="640" /></a></div><br /><div><span style="background-color: white; color: #222222; font-size: 15.4px;"><span style="font-family: inherit;">Lets explore Azure portal to see if the message has arrived.</span></span></div><div style="background-color: white; color: #222222; font-size: 15.4px;"><span style="font-family: inherit;">Go to Service Bus "DemoAZSvcBus", select Queues and open "CorpAnnouncements" Topic.</span></div><div>You can see that the message we sent has been pused to all Subscriptions. In our case "NZSubscription".</div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-bCFiOKr8VFA/YJMsTaYaSzI/AAAAAAAAH0Q/jAU92S5U-fs_RPgBg1-og4QyHbLTvdPawCLcBGAsYHQ/s2221/6.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="820" data-original-width="2221" height="236" src="https://1.bp.blogspot.com/-bCFiOKr8VFA/YJMsTaYaSzI/AAAAAAAAH0Q/jAU92S5U-fs_RPgBg1-og4QyHbLTvdPawCLcBGAsYHQ/w640-h236/6.png" width="640" /></a></div><br /> Unlike Queue, the messages in Topics will be tunnel to subscriptions and they will stay there for the subscribers to consume. They stay there until message timeout.</div><div><br /></div><div><div><b>Step 4: Create CorpAnnouncementReceiver (Service Bus Topic Message Receiver)</b></div><div>Open Shell, navigate to "ServiceBus" folder. Run below commands.</div><div><br /></div><div><pre>dotnet new console -n CorpAnnouncementReceiver<br />CD .\CorpAnnouncementReceiver\<br />dotnet add package Microsoft.Azure.ServiceBus<br />Code .</pre></div><div>Now copy below code in program.cs file.</div><div><pre>using System;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Azure.ServiceBus;
namespace CorpAnnouncementReceiver
{
class Program
{
const string ServiceBusConnectionString = "<span style="background-color: #fcff01;">Service Bus Connection string here</span>";<br />
const string TopicName = "corpannouncements";
const string SubscriptionName = "NZSubscription";
static ISubscriptionClient subscriptionClient;
static void Main(string[] args)
{
MainAsync().GetAwaiter().GetResult();
}
static async Task MainAsync()
{
subscriptionClient = new SubscriptionClient(ServiceBusConnectionString, TopicName, SubscriptionName);
Console.WriteLine("======================================================");
Console.WriteLine("Press ENTER key to exit after receiving all the messages.");
Console.WriteLine("======================================================");
RegisterMessageHandler();
Console.Read();
await subscriptionClient.CloseAsync();
}
static void RegisterMessageHandler()
{
var messageHandlerOptions = new MessageHandlerOptions(ExceptionReceivedHandler)
{
MaxConcurrentCalls = 1,
AutoComplete = false
};
subscriptionClient.RegisterMessageHandler(ProcessMessagesAsync, messageHandlerOptions);
}
static async Task ProcessMessagesAsync(Message message, CancellationToken token)
{
Console.WriteLine($"Received corp announcement message: SequenceNumber:{message.SystemProperties.SequenceNumber} Body:{Encoding.UTF8.GetString(message.Body)}");
//Uncomment below line if you want to delete the message from subscription
//await subscriptionClient.CompleteAsync(message.SystemProperties.LockToken);
}
static Task ExceptionReceivedHandler(ExceptionReceivedEventArgs exceptionReceivedEventArgs)
{
Console.WriteLine($"Message handler encountered an exception {exceptionReceivedEventArgs.Exception}.");
var context = exceptionReceivedEventArgs.ExceptionReceivedContext;
Console.WriteLine("Exception context for troubleshooting:");
Console.WriteLine($"- Endpoint: {context.Endpoint}");
Console.WriteLine($"- Entity Path: {context.EntityPath}");
Console.WriteLine($"- Executing Action: {context.Action}");
return Task.CompletedTask;
}
}
}</pre></div><div><div>Save program.cs file, open shell and run </div><div><pre>dotnet build</pre></div><div>and now navigate to "ServiceBus" folder, run below command.</div><div><pre>dotnet run -p CorpMessageReceiverr</pre></div><div>You will see the result like this.</div></div></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-xJEDD9Tv9Og/YJNIW9fXYBI/AAAAAAAAH0Y/audxVhGFwpER1xPn_req0-cR715nwzdpQCLcBGAsYHQ/s1311/7.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="594" data-original-width="1311" height="290" src="https://1.bp.blogspot.com/-xJEDD9Tv9Og/YJNIW9fXYBI/AAAAAAAAH0Y/audxVhGFwpER1xPn_req0-cR715nwzdpQCLcBGAsYHQ/w640-h290/7.png" width="640" /></a></div><br /><div>The Message in subcription will not be deleted unless you delete them exclusively using your code.</div><div>I have commented the line of code which deletes the message. Your programatic choice.</div><div>Lets check azure portal is the message still there even after received by the subscriber components.</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-zsrZnSkYifs/YJNLPEcc3UI/AAAAAAAAH0g/0-19UQXxOlQVcsSatrrVQTQ4uR35fWKbgCLcBGAsYHQ/s2221/6.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="820" data-original-width="2221" height="236" src="https://1.bp.blogspot.com/-zsrZnSkYifs/YJNLPEcc3UI/AAAAAAAAH0g/0-19UQXxOlQVcsSatrrVQTQ4uR35fWKbgCLcBGAsYHQ/w640-h236/6.png" width="640" /></a></div><br /><div>Typically your messages will not be a text message like mine, but in realtime, it will be a JSON message possibley with a data object embedded into it.</div><div><br /></div><div>I will Attach the source code for all 4 projects here. <a href="https://1drv.ms/u/s!AmYiNWkD2PZWox6-Wpb5DqTyIq3K?e=rM0Gs1" target="_blank">Download</a></div><div> </div>Pratap Reddy Pilakahttp://www.blogger.com/profile/17141976956878163883noreply@blogger.com0tag:blogger.com,1999:blog-4208378482021219922.post-39777608654972316292021-05-05T10:52:00.008-07:002021-05-05T15:56:20.036-07:00Azure Service Bus - Queues and Topics - Part 1<p><b>Use Case:</b> Organization X has a large-scale distributed serverless application environment. There are some azure components that sends a private messages which need to be accessed only once by other components using RoleBasedAccessControls. There are some messages that need to Fan-Out(one-to-many) to large number of systems, where sender doesnt need to know the receiver's details. The message should stay until time out, for receiving components to consume when they are avilable.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-teZJikKqfl4/YJLTW7Y_D_I/AAAAAAAAHx8/ENWFQImBCyceqMQPBYBaj4rohC32mqQjQCLcBGAsYHQ/s1179/1.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="677" data-original-width="1179" height="368" src="https://1.bp.blogspot.com/-teZJikKqfl4/YJLTW7Y_D_I/AAAAAAAAHx8/ENWFQImBCyceqMQPBYBaj4rohC32mqQjQCLcBGAsYHQ/w640-h368/1.png" width="640" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div>In this article, we will learn what is Azure Service Bus, Service Bus Queues, Service Bus Topics, How to access them programatically using .Net. Once you do it practically following my instrctions, you will understand the theory behind it. So follow me.</div><div style="font-weight: bold;"><b><br /></b></div><b>Why Azure Service Bus?</b><div>Service Bus is a message broker which handles Integrity, Security, Communication protocols of Messages at Enterprise level.</div><a name='more'></a><div><br /></div><div>It has both Queues and Topics for message transmission. We will see each of it using .Net Console apps as both sender and receiver.</div><div><b style="color: #222222; font-size: 15.4px;"><span style="font-family: inherit;"><br /></span></b></div><div><b style="color: #222222; font-size: 15.4px;"><span style="font-family: inherit;">Use Service Bus queues if you:</span></b></div><div><p style="background-color: white; color: #222222; font-size: 15.4px;"></p><ul style="background-color: white; color: #222222; font-size: 15.4px; line-height: 1.4; margin: 0.5em 0px; padding: 0px 2.5em;"><li style="margin: 0px 0px 0.25em; padding: 0px;"><span style="font-family: inherit;">Need an At-Most-Once delivery guarantee.</span></li><li style="margin: 0px 0px 0.25em; padding: 0px;"><span style="font-family: inherit;">Need a FIFO guarantee.</span></li><li style="margin: 0px 0px 0.25em; padding: 0px;"><span style="font-family: inherit;">Need to group messages into transactions.</span></li><li style="margin: 0px 0px 0.25em; padding: 0px;"><span style="font-family: inherit;">Want to receive messages without polling the queue.</span></li><li style="margin: 0px 0px 0.25em; padding: 0px;"><span style="font-family: inherit;">Need to provide a role-based access model to the queues.</span></li><li style="margin: 0px 0px 0.25em; padding: 0px;"><span style="font-family: inherit;">Need to handle messages larger than 64 KB but less than 256 KB.</span></li><li style="margin: 0px 0px 0.25em; padding: 0px;"><span style="font-family: inherit;">Queue size will not grow larger than 80 GB.</span></li><li style="margin: 0px 0px 0.25em; padding: 0px;"><span style="font-family: inherit;">Want to publish and consume batches of messages.</span></li></ul></div><div><p style="background-color: white; color: #222222; font-size: 15.4px;"><b style="background-color: transparent;">Step 1: Create Azure Service Bus </b></p></div><div><span style="color: #222222;"><span style="font-size: 15.4px;">Go to Azure portal, always start with a resource group. I named it "DefaultRG".</span></span></div><div><span style="color: #222222;"><span style="font-size: 15.4px;">Next Create a Service Bus, i named it "DemoAZSvcBus".</span></span></div><div><span style="color: #222222;"><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-dycy6l9m9pk/YJLAXTI4qWI/AAAAAAAAHw0/42263KZ715s3DhIytrSViaiEyuBZd-LqACLcBGAsYHQ/s1137/2.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="807" data-original-width="1137" height="454" src="https://1.bp.blogspot.com/-dycy6l9m9pk/YJLAXTI4qWI/AAAAAAAAHw0/42263KZ715s3DhIytrSViaiEyuBZd-LqACLcBGAsYHQ/w640-h454/2.png" width="640" /></a></div>Once Service Bus is created, in the left pannel, under "Entities", click on Queues.</span></div><div><span style="color: #222222;">Create a new Queue, name it "CorpMessages".</span></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-ftw3OrFjhyo/YJLA1GPccmI/AAAAAAAAHw8/uInb6HwCgvwdpkh3KdeMgkmKSPKnyuJxgCLcBGAsYHQ/s648/3.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="566" data-original-width="648" height="350" src="https://1.bp.blogspot.com/-ftw3OrFjhyo/YJLA1GPccmI/AAAAAAAAHw8/uInb6HwCgvwdpkh3KdeMgkmKSPKnyuJxgCLcBGAsYHQ/w400-h350/3.png" width="400" /></a></div><br /><span style="color: #222222;">This is the queue we will use to send the private messages to other intended receiving systems.</span></div><div><br /></div><div>For all the following steps, you need to install Azure CLI, .Net, Powershell 7, VS Code on your dev machines.</div><div><br /></div><div><b>Step 2: Get Connection string for Azure Service Bus Namespace.</b></div><div>It costs money to create a storage account in azure, especially when you are using free tier or experimenting on your own. Thats why i use Azure CLI on Powershell 7.</div><div><br /></div><div>Open Power shell and run below commands. After first command, you may need to authenticate to Azure console via browser, then close the browser. Make sure you check update Resource group name and Service Bus name in your command.</div><div><pre>az login <br />az servicebus namespace authorization-rule keys list --resource-group <span style="background-color: #fcff01;">DefaultRG</span> --name RootManageSharedAccessKey --query primaryConnectionString --output tsv --namespace-name <span style="background-color: #fcff01;">DemoAZSvcBus</span></pre></div><div>This command will bring back the connection string which you need to keep it aside for following steps. It will be in this formatt.</div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-snOPU_2kcNA/YJLG44xAOnI/AAAAAAAAHxc/GzB2DhjKo8k7VKwq41PP4FpbkC48sgzpQCLcBGAsYHQ/s1344/6.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="220" data-original-width="1344" height="104" src="https://1.bp.blogspot.com/-snOPU_2kcNA/YJLG44xAOnI/AAAAAAAAHxc/GzB2DhjKo8k7VKwq41PP4FpbkC48sgzpQCLcBGAsYHQ/w640-h104/6.png" width="640" /></a></div><br /><div><b>Step 3: Create CorpMessageSender (Service Bus Queue Message Sender)</b></div><div>Lets start with a folder "ServiceBus". All the projects from here on will be created inside it.</div><div>Open Shell, navigate to "ServiceBus" folder. Run below commands one after another.</div><div><br /></div><div><pre>dotnet new console -n CorpMessageSender<br />CD .\CorpMessageSender\<br />dotnet add package Microsoft.Azure.ServiceBus<br />Code .</pre></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-gye5GcZOKBs/YJLHTYZxSaI/AAAAAAAAHxk/U3Urr43BEDYkEZUooyA6jf1UXhI8F8bNwCLcBGAsYHQ/s1299/7.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="374" data-original-width="1299" height="184" src="https://1.bp.blogspot.com/-gye5GcZOKBs/YJLHTYZxSaI/AAAAAAAAHxk/U3Urr43BEDYkEZUooyA6jf1UXhI8F8bNwCLcBGAsYHQ/w640-h184/7.png" width="640" /></a></div><div><div><br /></div><div>These command will create a new .Net Console application, then install Azure Service Bus package and open the project in VS Code.</div><div><br /></div><div>Copy below code to program.cs. You may need to write similar code in your azure component which is sending out secured, transcation bound messages to other systems.</div><div><br /></div><div><pre>using System;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Azure.ServiceBus;
namespace CorpMessageSender
{
class Program
{
const string ServiceBusConnectionString = "<span style="background-color: #fcff01;">Service Bus Connection string here</span>";
const string QueueName = "corpmessages";
static IQueueClient queueClient;
static void Main(string[] args)
{
Console.WriteLine("Sending a message to the Corporate Messages queue...");
SendCorpMessageAsync().GetAwaiter().GetResult();
Console.WriteLine("Message was sent successfully.");
}
static async Task SendCorpMessageAsync()
{
//Creating Service Bus Queue client
queueClient = new QueueClient(ServiceBusConnectionString, QueueName);
try
{
string messageBody = $"Private message from CEO: Q1 Results: $1bn Profit";
var message = new Message(Encoding.UTF8.GetBytes(messageBody));
Console.WriteLine($"Sending message: {messageBody}");
//Sending the private message to Service bus Queue
await queueClient.SendAsync(message);
}
catch (Exception exception)
{
Console.WriteLine($"{DateTime.Now} :: Exception: {exception.Message}");
}
await queueClient.CloseAsync();
}
}
}</pre></div><div><div>Save program.cs file, open shell and run </div><div><pre>dotnet build</pre></div><div>and now navigate to "ServiceBus" folder, run below command.</div><div><pre>dotnet run -p CorpMessageSender</pre></div><div>You will see the result like this.</div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-jLT1cWk7QKw/YJLN54jJN_I/AAAAAAAAHxs/wru0iRrLzBQxFt5C3Nq5H0P6Hic-kzZfQCLcBGAsYHQ/s853/8.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="199" data-original-width="853" height="150" src="https://1.bp.blogspot.com/-jLT1cWk7QKw/YJLN54jJN_I/AAAAAAAAHxs/wru0iRrLzBQxFt5C3Nq5H0P6Hic-kzZfQCLcBGAsYHQ/w640-h150/8.png" width="640" /></a></div><br /><div>Lets explore Azure portal to see if the message has arrived.</div><div>Go to Service Bus "DemoAZSvcBus", select Queues and open "CorpMessages" Queue.</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-VvSvTnmpKL0/YJLO2sxOOcI/AAAAAAAAHx0/iPiDj5krpGUD1NjzjpPf2C7yTNPMF01ngCLcBGAsYHQ/s2228/9.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="731" data-original-width="2228" height="210" src="https://1.bp.blogspot.com/-VvSvTnmpKL0/YJLO2sxOOcI/AAAAAAAAHx0/iPiDj5krpGUD1NjzjpPf2C7yTNPMF01ngCLcBGAsYHQ/w640-h210/9.png" width="640" /></a></div><br /><div><b>Step 4: Create CorpMessageReceiver (Service Bus Queue Message Receiver)</b></div><div>Open Shell, navigate to "ServiceBus" folder. Run below commands.</div><div><br /></div><div><div><pre>dotnet new console -n CorpMessageSender<br />CD .\CorpMessageSender\<br />dotnet add package Microsoft.Azure.ServiceBus<br />Code .</pre></div></div><div>Now copy below code in program.cs file.</div><div><pre>using System;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Azure.ServiceBus;
namespace CorpMessageReceiver
{
class Program
{
const string ServiceBusConnectionString = "<span style="background-color: #fcff01;">Service Bus Connection string here</span>";<br />
const string QueueName = "corpmessages";
static IQueueClient queueClient;
static void Main(string[] args)
{
ReceiveCorpMessageAsync().GetAwaiter().GetResult();
}
static async Task ReceiveCorpMessageAsync()
{
queueClient = new QueueClient(ServiceBusConnectionString, QueueName);
Console.WriteLine("======================================================");
Console.WriteLine("Press ENTER key to exit after receiving all the messages.");
Console.WriteLine("======================================================");
RegisterMessageHandler();
Console.Read();
await queueClient.CloseAsync();
}
static void RegisterMessageHandler()
{
var messageHandlerOptions = new MessageHandlerOptions(ExceptionReceivedHandler)
{
MaxConcurrentCalls = 1,
AutoComplete = false
};
queueClient.RegisterMessageHandler(ProcessMessagesAsync, messageHandlerOptions);
}
static async Task ProcessMessagesAsync(Message message, CancellationToken token)
{
Console.WriteLine($"Received message: SequenceNumber:{message.SystemProperties.SequenceNumber} Body:{Encoding.UTF8.GetString(message.Body)}");
await queueClient.CompleteAsync(message.SystemProperties.LockToken);
}
static Task ExceptionReceivedHandler(ExceptionReceivedEventArgs exceptionReceivedEventArgs)
{
Console.WriteLine($"Message handler encountered an exception {exceptionReceivedEventArgs.Exception}.");
var context = exceptionReceivedEventArgs.ExceptionReceivedContext;
Console.WriteLine("Exception context for troubleshooting:");
Console.WriteLine($"- Endpoint: {context.Endpoint}");
Console.WriteLine($"- Entity Path: {context.EntityPath}");
Console.WriteLine($"- Executing Action: {context.Action}");
return Task.CompletedTask;
}
}
}</pre></div><div><div>Save program.cs file, open shell and run </div><div><pre>dotnet build</pre></div><div>and now navigate to "ServiceBus" folder, run below command.</div><div><pre>dotnet run -p CorpMessageReceiverr</pre></div><div>You will see the result like this.</div></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-j-O-ya2mzuE/YJLYwCh7GTI/AAAAAAAAHyE/fI7568-_wIwh9_H5W1mchaX9BnylvziuACLcBGAsYHQ/s1204/10.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="229" data-original-width="1204" height="122" src="https://1.bp.blogspot.com/-j-O-ya2mzuE/YJLYwCh7GTI/AAAAAAAAHyE/fI7568-_wIwh9_H5W1mchaX9BnylvziuACLcBGAsYHQ/w640-h122/10.png" width="640" /></a></div><br /><div>Message received, and this message will be deleted from Service Bus Queue, since the intended receiver have consumed the message.</div><div>You may need to write similar code in your receiving component.</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-IlIqEROJp9U/YJLZgMQMJjI/AAAAAAAAHyM/ep5bTIQ2hJ0A3-SbnSnut1MnHfir2dw8gCLcBGAsYHQ/s954/11.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="518" data-original-width="954" height="348" src="https://1.bp.blogspot.com/-IlIqEROJp9U/YJLZgMQMJjI/AAAAAAAAHyM/ep5bTIQ2hJ0A3-SbnSnut1MnHfir2dw8gCLcBGAsYHQ/w640-h348/11.png" width="640" /></a></div><br /><div>We have finished Azure Service Bus Queues. In nest article Part-2, we will programatically send and recieve messages to Azure Service Topics and observe thier behaviour.</div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-iiNJpnpQo8U/YJLbRf06ufI/AAAAAAAAHyU/7rcyJNMOaFQxdergDMODl98ZAIlv7hDyACLcBGAsYHQ/s1179/12.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="677" data-original-width="1179" height="368" src="https://1.bp.blogspot.com/-iiNJpnpQo8U/YJLbRf06ufI/AAAAAAAAHyU/7rcyJNMOaFQxdergDMODl98ZAIlv7hDyACLcBGAsYHQ/w640-h368/12.png" width="640" /></a></div><br /><div><br /></div><div><br /></div></div></div>Pratap Reddy Pilakahttp://www.blogger.com/profile/17141976956878163883noreply@blogger.com0tag:blogger.com,1999:blog-4208378482021219922.post-13550581844729854682021-05-05T03:51:00.001-07:002021-05-05T03:51:30.894-07:00Azure Messaging Models<p> All of this is straight from Microsoft Documentation. I am just grouping them for better reference. </p><p>Every time i say, "<b>This is what Microsoft says, and then i validate the theory with a practicle implementation</b>". This time, this article is full of thoery, so this is all about what Microsoft says. I take no credit for this infromation.</p><a name='more'></a><p><b>What is a message?</b></p><p>In the terminology of distributed applications, messages have the following characteristics:</p><p></p><ul style="text-align: left;"><li>A message contains raw data, produced by one component, that will be consumed by another component.</li><li>A message contains the data itself, not just a reference to that data.</li><li>The sending component expects the message content to be processed in a certain way by the destination component. The integrity of the overall system may depend on both sender and receiver doing a specific job.</li></ul><p></p><p><b>What is an event?</b></p><p>Events are lighter weight than messages, and are most often used for broadcast communications. The components sending the event are known as publishers, and receivers are known as subscribers.</p><p>Events have the following characteristics:</p><p></p><ul style="text-align: left;"><li>An event is a lightweight notification that indicates that something happened.</li><li>The event may be sent to multiple receivers, or to none at all.</li><li>Events are often intended to "fan out," or have a large number of subscribers for each publisher.</li><li>The publisher of the event has no expectation about the action a receiving component takes.</li><li>Some events are discrete units and unrelated to other events.</li><li>Some events are part of a related and ordered series.</li></ul><p></p><p><b>Use Queue storage if you:</b></p><p></p><ul style="text-align: left;"><li>Need an audit trail of all messages that pass through the queue.</li><li>Expect the queue to exceed 80 GB in size.</li><li>Want to track progress for processing a message inside of the queue.</li></ul><p></p><p><b>Use Service Bus topics if you:</b></p><p></p><ul style="text-align: left;"><li>Need multiple receivers to handle each message</li></ul><p></p><p><b>Use Service Bus queues if you:</b></p><p></p><ul style="text-align: left;"><li>Need an At-Most-Once delivery guarantee.</li><li>Need a FIFO guarantee.</li><li>Need to group messages into transactions.</li><li>Want to receive messages without polling the queue.</li><li>Need to provide a role-based access model to the queues.</li><li>Need to handle messages larger than 64 KB but less than 256 KB.</li><li>Queue size will not grow larger than 80 GB.</li><li>Want to publish and consume batches of messages.</li></ul><p></p><p><b>What is Azure Event Grid?</b></p><p>Azure Event Grid is a fully-managed event routing service running on top of Azure Service Fabric. Event Grid distributes events from different sources, such as Azure Blob storage accounts or Azure Media Services, to different handlers, such as Azure Functions or Webhooks. Event Grid was created to make it easier to build event-based and serverless applications on Azure.</p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://lh3.googleusercontent.com/-IrPhrlvlpyw/YJJ4G9nJkyI/AAAAAAAAHwk/NZYQ2PodJmU8jhdq62B7WdXC84Kx10xkwCLcBGAsYHQ/image.png" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="359" data-original-width="570" height="404" src="https://lh3.googleusercontent.com/-IrPhrlvlpyw/YJJ4G9nJkyI/AAAAAAAAHwk/NZYQ2PodJmU8jhdq62B7WdXC84Kx10xkwCLcBGAsYHQ/w640-h404/image.png" width="640" /></a></div><br />There are several concepts in Azure Event Grid that connect a source to a subscriber:<p></p><p><b>Events:</b> What happened.</p><p><b>Event sources:</b> Where the event took place.</p><p><b>Topics: </b>The endpoint where publishers send events.</p><p><b>Event subscriptions:</b> The endpoint or built-in mechanism to route events, sometimes to multiple handlers. Subscriptions are also used by handlers to filter incoming events intelligently.</p><p><b>Event handlers:</b> The app or service reacting to the event.</p><p><b>What is Azure Event Hubs?</b></p><p>Event Hubs is an intermediary for the publish-subscribe communication pattern. Unlike Event Grid, however, it is optimized for extremely high throughput, a large number of publishers, security, and resiliency.</p><p><b>Choose Event Hubs if:</b></p><p></p><ul style="text-align: left;"><li>You need to support authenticating a large number of publishers.</li><li>You need to save a stream of events to Data Lake or Blob storage.</li><li>You need aggregation or analytics on your event stream.</li><li>You need reliable messaging or resiliency.</li></ul><p></p><p>Otherwise, if you need a simple event publish-subscribe infrastructure, with trusted publishers (for instance, your own web server), you should choose Event Grid.</p><p>Event Hubs lets you build a big data pipeline capable of processing millions of events per second with low latency. It can handle data from concurrent sources and route it to a variety of stream-processing infrastructures and analytics services. It enables real-time processing and supports repeated replay of stored raw data.</p>Pratap Reddy Pilakahttp://www.blogger.com/profile/17141976956878163883noreply@blogger.com0tag:blogger.com,1999:blog-4208378482021219922.post-18203254215902938552021-05-03T23:01:00.009-07:002021-05-03T23:02:31.337-07:00Azure Durable Functions - Starter, Orchestrator and Activity Fucntions<p><b>Use Case: </b>A user starts to book a trip, which consists booking a Flight and Hotel and then pay for complete trip. </p><p>Back in the day, the answer used to be a single server-side application which is complex enough to take care of everything. Now we call it <b>Monolith</b> application. </p><p>Now we use microservices architecture to seperate the concerns at domain level. With Function as a Service, it even evolved to a Serverless state, where you can provision the services even without using any infrastructure.</p><p><b>Azure Functions</b> is one of the critical components of serverless designs, and here is what we know about them:</p><p><b>Even-Driven:</b> Azure functions are invoked based on a trigger caused by events like making a http call (or) adding a message to a Queue (or) adding a object to a blob storage (or) adding a new record to CosmosDB and many others.</p><p><b>Short-Lived: </b>Azure functions are meant to for short period of excutions and to address a simple tasks which makes it more suitable for Microservices part of the design. Keep it simple and seperated.</p><p><b>Stateless: </b>Azure Functions doesnt save any state infirmation of objects, thus the trigger mechanism will have both inputs and outputs. </p><p>Now considering above facts, i have created a design for above Use Case.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-bX2VYjgf1xo/YJDKbnoWUyI/AAAAAAAAHuY/nAUZLtBkPLUVTxgvk74EwJHjjyZQ_9yewCLcBGAsYHQ/s1180/1.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="528" data-original-width="1180" height="286" src="https://1.bp.blogspot.com/-bX2VYjgf1xo/YJDKbnoWUyI/AAAAAAAAHuY/nAUZLtBkPLUVTxgvk74EwJHjjyZQ_9yewCLcBGAsYHQ/w640-h286/1.png" width="640" /></a></div>Now the deisgn is pretty much simple and self-explanatory. <span style="color: red;"><b>But here is the catch.</b><a name='more'></a></span><div><br /></div><div>Inorder to confrim booking to the user, both flight and hotel bokings need to be done to finalize the cost of the trip. This means "Confirm Booking" should execute only when both "Book Flight" and "Book Hotel" are sucessful. </div><div><br /></div><div>Give a thought for a minute how you going to handle in your opinion.</div><div><br /></div><div><b>Here are the problems: </b></div><div>1. Both "Book Flights" and "Book Hotel" functions might not be executed at the same millisecond. YOu are not sure which one will finish first.</div><div>2. One of the functions might not be avilable right away. Due to the fact Azure function instances were created for every 10 seconds based on demand.</div><div>3. What if one of the function gave an error.</div><div>4. How do you orchestrate the out put of two functions withholding data until both of them were finiched exceuting. Remeber Fucntions are stateless. </div><div><br /></div><div><b>Durable Fucntions: </b><span style="font-family: inherit;">Durable Functions<span style="background-color: white;"> is a library that brings workflow orchestration abstractions to Azure Functions. It introduces a number of idioms and tools to define stateful, potentially long-running operations, and manages a lot of mechanics of reliable communication and state management behind the scenes.</span></span></div><div><br /></div><div>Says Microsoft, but let us implement one and show you what it means.</div><div><br /></div><div><b>Step 1:</b> Create a Azure Function App.</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-GWASzWZluUM/YJDO2cp_SpI/AAAAAAAAHug/Mm4HZXIz3vQsrdYeoAyscyamvh3UwPOBACLcBGAsYHQ/s1160/2.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1061" data-original-width="1160" height="586" src="https://1.bp.blogspot.com/-GWASzWZluUM/YJDO2cp_SpI/AAAAAAAAHug/Mm4HZXIz3vQsrdYeoAyscyamvh3UwPOBACLcBGAsYHQ/w640-h586/2.png" width="640" /></a></div><br /><div>Durable Function is a open source package embraced by micrisoft azure. For this we need to install the package. For that we need to create "package.json" file. This file will hold information of all the packages installed.</div><div><br /></div><div>Open the Function App and in left hand menu click on <b>"Console"</b> in "Development Tools" category. </div><div>Run below command.</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-gNb9sJyI5D4/YJDQfvDVawI/AAAAAAAAHuo/kmciXW83x0M1-YeJz8DKGLdqCshV8fk6ACLcBGAsYHQ/s526/3.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="343" data-original-width="526" height="261" src="https://1.bp.blogspot.com/-gNb9sJyI5D4/YJDQfvDVawI/AAAAAAAAHuo/kmciXW83x0M1-YeJz8DKGLdqCshV8fk6ACLcBGAsYHQ/w400-h261/3.png" width="400" /></a></div><br /><div>This will create a new file "package.json". Now click on <b>"App Files"</b> in "Fucntions" category. Open the package.json file and add below code.</div><div><pre>{<div style="background-color: #fffffe; font-family: Consolas, "Courier New", monospace; font-size: 14px; line-height: 19px; white-space: pre;"><div> <span style="color: #a31515;">"name"</span>: <span style="color: #0451a5;">"DuraFuctionDemo"</span>,</div><div> <span style="color: #a31515;">"version"</span>: <span style="color: #0451a5;">"1.0.0"</span></div></div><span style="background-color: #fffffe; font-family: Consolas, "Courier New", monospace; font-size: 14px;">}</span></pre>Save the chnages and go to overview and restart the Function App.</div><div><br /></div><div><b>Step 2: </b> Install required packages.</div><div>Open Console again and run "<i>npm install durable-functions</i>". This might generate a new file with name "package-lock.json". Go to "App files" again and copy the content of "package-lock.json" file and paste it to "package.json" file and save.</div><div>Go to Overview and restart the Fucntion App again.</div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-UGBwCojiwD8/YJDTIjMA8qI/AAAAAAAAHuw/UDwo12r7JVcQoGhG7O_768Ng3l2jnQQ7ACLcBGAsYHQ/s1190/5.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="497" data-original-width="1190" height="268" src="https://1.bp.blogspot.com/-UGBwCojiwD8/YJDTIjMA8qI/AAAAAAAAHuw/UDwo12r7JVcQoGhG7O_768Ng3l2jnQQ7ACLcBGAsYHQ/w640-h268/5.png" width="640" /></a></div><br /><div><b>Step 3:</b> Create Fucntions.</div><div>Durable functions has 3 roles.</div><div><br /></div><div>1. <b>Starter Fucntion</b> - Invocation to this function will be handed over to Orchestartor Function.</div><div>2. <b>Orchestartor Function</b> - This function will make calls to Activity Fucntions based on context.</div><div>3. <b>Activity Function</b> - This function will do actual excution of the task and send back the response back to Orchestartor Fucntion.</div><div><br /></div><div>Create a function, select "Durable Functions HTTP starter" as the template.</div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-Dw307FPculc/YJDYChpstSI/AAAAAAAAHu8/vSDci8l5dcQUFIvJfOJsHmN99Zw56BmOgCLcBGAsYHQ/s800/6.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="719" data-original-width="800" height="360" src="https://1.bp.blogspot.com/-Dw307FPculc/YJDYChpstSI/AAAAAAAAHu8/vSDci8l5dcQUFIvJfOJsHmN99Zw56BmOgCLcBGAsYHQ/w400-h360/6.png" width="400" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div>Leave the code as is.</div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-GizE9rsjs8c/YJDY8Fj4LyI/AAAAAAAAHvI/4jav03bKcDsc6O_B8m9cjdLcL4w_g25awCLcBGAsYHQ/s1559/10.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="533" data-original-width="1559" height="218" src="https://1.bp.blogspot.com/-GizE9rsjs8c/YJDY8Fj4LyI/AAAAAAAAHvI/4jav03bKcDsc6O_B8m9cjdLcL4w_g25awCLcBGAsYHQ/w640-h218/10.png" width="640" /></a></div><br /><div>Create 1st Activity Function.</div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-WjxhnRJKwNc/YJDZJA6bHaI/AAAAAAAAHvM/3tz9GZyiHQ4_dWzeqyc9k0YppGvHrmXlACLcBGAsYHQ/s787/7.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="630" data-original-width="787" height="320" src="https://1.bp.blogspot.com/-WjxhnRJKwNc/YJDZJA6bHaI/AAAAAAAAHvM/3tz9GZyiHQ4_dWzeqyc9k0YppGvHrmXlACLcBGAsYHQ/w400-h320/7.png" width="400" /></a></div><br /><div>Paste below code in BookFlight function.</div><div><pre>module.exports = <span style="background-color: #fffffe; color: blue; font-family: Consolas, "Courier New", monospace; font-size: 14px;">async</span><span style="background-color: #fffffe; font-family: Consolas, "Courier New", monospace; font-size: 14px;"> </span><span style="background-color: #fffffe; color: blue; font-family: Consolas, "Courier New", monospace; font-size: 14px;">function</span><span style="background-color: #fffffe; font-family: Consolas, "Courier New", monospace; font-size: 14px;"> (context) {</span><div style="background-color: #fffffe; font-family: Consolas, "Courier New", monospace; font-size: 14px; line-height: 19px; white-space: pre;"><div> <span style="color: blue;">return</span> <span style="color: #a31515;">`Flight Bookings for the trip have been - </span>${context.bindings.name}<span style="color: #a31515;">!`</span>;</div></div><span style="background-color: #fffffe; font-family: Consolas, "Courier New", monospace; font-size: 14px;">};</span></pre>Create 2nd Activity Function.</div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-McIL4NR7_Pk/YJDZ7wbu-BI/AAAAAAAAHvY/jJYvpZ5cB2c1YQHZ166DFn4At6FXD-hngCLcBGAsYHQ/s775/8.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="643" data-original-width="775" height="331" src="https://1.bp.blogspot.com/-McIL4NR7_Pk/YJDZ7wbu-BI/AAAAAAAAHvY/jJYvpZ5cB2c1YQHZ166DFn4At6FXD-hngCLcBGAsYHQ/w400-h331/8.png" width="400" /></a></div><br /><div>Paste below code in BookHotel function.</div><div><pre>module.exports = <span style="background-color: #fffffe; color: blue; font-family: Consolas, "Courier New", monospace; font-size: 14px;">async</span><span style="background-color: #fffffe; font-family: Consolas, "Courier New", monospace; font-size: 14px;"> </span><span style="background-color: #fffffe; color: blue; font-family: Consolas, "Courier New", monospace; font-size: 14px;">function</span><span style="background-color: #fffffe; font-family: Consolas, "Courier New", monospace; font-size: 14px;"> (context) {</span><div style="background-color: #fffffe; font-family: Consolas, "Courier New", monospace; font-size: 14px; line-height: 19px; white-space: pre;"><div> <span style="color: blue;">return</span> <span style="color: #a31515;">`Hotel Bookings for the trip have been - </span>${context.bindings.name}<span style="color: #a31515;">!`</span>;</div></div><span style="background-color: #fffffe; font-family: Consolas, "Courier New", monospace; font-size: 14px;">};</span></pre>Create Orchestrator function.</div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/--Zfiv1Ottj0/YJDalmFxgfI/AAAAAAAAHvg/u9i8-XRk10IsHMnRNfJCNG_aKFZxTUHRQCLcBGAsYHQ/s783/9.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="548" data-original-width="783" height="280" src="https://1.bp.blogspot.com/--Zfiv1Ottj0/YJDalmFxgfI/AAAAAAAAHvg/u9i8-XRk10IsHMnRNfJCNG_aKFZxTUHRQCLcBGAsYHQ/w400-h280/9.png" width="400" /></a></div><br /><div>Paste below code in "BookingOrchestrator" function.</div><div><pre><b>const</b><span style="background-color: #fffffe; font-family: Consolas, "Courier New", monospace; font-size: 14px;"> df = require(</span><span style="background-color: #fffffe; color: #a31515; font-family: Consolas, "Courier New", monospace; font-size: 14px;">"durable-functions"</span><span style="background-color: #fffffe; font-family: Consolas, "Courier New", monospace; font-size: 14px;">);</span><div style="background-color: #fffffe; font-family: Consolas, "Courier New", monospace; font-size: 14px; line-height: 19px; white-space: pre;"><br /><div>module.exports = df.orchestrator(<span style="color: blue;">function</span>* (context) {</div><div> <span style="color: blue;">const</span> outputs = [];</div><br /><div> <span style="color: green;">// Asyncronously calls both activity functions.</span></div><div> outputs.push(<span style="color: blue;">yield</span> context.df.callActivity(<span style="color: #a31515;">"BookFlight"</span>, <span style="color: #a31515;">"Confirmed"</span>));</div><div> outputs.push(<span style="color: blue;">yield</span> context.df.callActivity(<span style="color: #a31515;">"BookHotel"</span>, <span style="color: #a31515;">"Confirmed"</span>));</div><div> </div><div> </div><div> <span style="color: blue;">return</span> outputs;</div></div><span style="background-color: #fffffe; font-family: Consolas, "Courier New", monospace; font-size: 14px;">});</span></pre>We all all set for execution.</div><div><br /></div><div><b>Step 4: </b>Test the functionality.</div><div>Open Starter Function, click on "Test/Run" and give the orchestrator function name as parameter.</div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-rd9mUfNIfBM/YJDcNdsjroI/AAAAAAAAHvo/uBHa1NkikCUtT0_YTiF3PSfUa2niB7cGgCLcBGAsYHQ/s930/11.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="237" data-original-width="930" height="103" src="https://1.bp.blogspot.com/-rd9mUfNIfBM/YJDcNdsjroI/AAAAAAAAHvo/uBHa1NkikCUtT0_YTiF3PSfUa2niB7cGgCLcBGAsYHQ/w400-h103/11.png" width="400" /></a></div><br /><div>or you can copy the function url from "Get Function URL" from overview and replce below highlighted function name with orchestrator name.</div><div><br /></div><div><span face="az_ea_font, "Segoe UI", az_font, system-ui, -apple-system, BlinkMacSystemFont, Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif" style="background-color: white; font-size: 16px;">https://durafunctiondemo.azurewebsites.net/api/orchestrators/</span><span face="az_ea_font, "Segoe UI", az_font, system-ui, -apple-system, BlinkMacSystemFont, Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif" style="background-color: #fcff01; font-size: 16px;">{functionName}</span><span face="az_ea_font, "Segoe UI", az_font, system-ui, -apple-system, BlinkMacSystemFont, Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif" style="background-color: white; font-size: 16px;">?code=oPaGqBv7jRVOxQjuJkiEtA/caBxb6Py4IzHCW0iE/okQXfDEi1k9kw==</span><br /><p>This is the response and the <b>"<span style="white-space: pre-wrap;">statusQueryGetUri</span>" </b>will give the final response combining the responses of all activity functions.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-_tju4R3r6Oo/YJDc-W8zLAI/AAAAAAAAHvw/ByPVKW4tbYcdfIFK-d7ET8N15pLXtHdFwCLcBGAsYHQ/s1795/12.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="449" data-original-width="1795" height="160" src="https://1.bp.blogspot.com/-_tju4R3r6Oo/YJDc-W8zLAI/AAAAAAAAHvw/ByPVKW4tbYcdfIFK-d7ET8N15pLXtHdFwCLcBGAsYHQ/w640-h160/12.png" width="640" /></a></div>Here is the final response from Orchestrator function.</div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-tcKim5Hxi9E/YJDdo6-grLI/AAAAAAAAHv4/Tjdq0ENo-dQ8Z5yNc25jOnJ9qRwSo4OiwCLcBGAsYHQ/s2084/13.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="212" data-original-width="2084" height="66" src="https://1.bp.blogspot.com/-tcKim5Hxi9E/YJDdo6-grLI/AAAAAAAAHv4/Tjdq0ENo-dQ8Z5yNc25jOnJ9qRwSo4OiwCLcBGAsYHQ/w640-h66/13.png" width="640" /></a></div><br /><div><b>Step 5:</b> What if one of the activity functions is not avilable.</div><div>Lets Disable "BookHotel" activity function.</div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-oDmafUFDE8w/YJDeZG1oQdI/AAAAAAAAHwA/DGfmQS1lpEwZAIR2wX_iaOm0tnls8C5fwCLcBGAsYHQ/s1358/14.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="430" data-original-width="1358" height="202" src="https://1.bp.blogspot.com/-oDmafUFDE8w/YJDeZG1oQdI/AAAAAAAAHwA/DGfmQS1lpEwZAIR2wX_iaOm0tnls8C5fwCLcBGAsYHQ/w640-h202/14.png" width="640" /></a></div><br /><div>Now repeat the Step 4 but browsing the url <span face="az_ea_font, "Segoe UI", az_font, system-ui, -apple-system, BlinkMacSystemFont, Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif" style="background-color: white; font-size: 16px;">https://durafunctiondemo.azurewebsites.net/api/orchestrators/</span><span face="az_ea_font, "Segoe UI", az_font, system-ui, -apple-system, BlinkMacSystemFont, Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif" style="background-color: #fcff01; font-size: 16px;">{functionName}</span><span face="az_ea_font, "Segoe UI", az_font, system-ui, -apple-system, BlinkMacSystemFont, Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif" style="background-color: white; font-size: 16px;">?code=oPaGqBv7jRVOxQjuJkiEtA/caBxb6Py4IzHCW0iE/okQXfDEi1k9kw==</span></div><div><span face="az_ea_font, Segoe UI, az_font, system-ui, -apple-system, BlinkMacSystemFont, Roboto, Oxygen, Ubuntu, Cantarell, Open Sans, Helvetica Neue, sans-serif"><br /></span></div><div><span style="font-family: inherit;">and then browse the url from </span><b>"<span style="white-space: pre-wrap;">statusQueryGetUri</span>".</b></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-2R9BRAAcSEo/YJDfK3htcJI/AAAAAAAAHwI/U1mBsZKdDCgJBwumhCwpSVxemJ18O8FVwCLcBGAsYHQ/s2108/15.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="211" data-original-width="2108" height="64" src="https://1.bp.blogspot.com/-2R9BRAAcSEo/YJDfK3htcJI/AAAAAAAAHwI/U1mBsZKdDCgJBwumhCwpSVxemJ18O8FVwCLcBGAsYHQ/w640-h64/15.png" width="640" /></a></div><br />You can see that the Orchestrator function ran the "BookFlight" function, but due to the fact "BookHotel" function is unavilable, its holding its state waiting for the response from "BookHotel" function.</div><div><br /></div><div>Question is where is the state information saved for time being.</div><div>Ans: Its in a Azure Queue with all related information. Let me show you.</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-a1IlZDdYc_0/YJDgP_uSTRI/AAAAAAAAHwQ/M1PwFFOIWVEt2R2W1UqiDXAG2OpQ9K_KQCLcBGAsYHQ/s1784/16.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="908" data-original-width="1784" height="326" src="https://1.bp.blogspot.com/-a1IlZDdYc_0/YJDgP_uSTRI/AAAAAAAAHwQ/M1PwFFOIWVEt2R2W1UqiDXAG2OpQ9K_KQCLcBGAsYHQ/w640-h326/16.png" width="640" /></a></div><br /><div>Now, lets enable the "BookHotel" activity fucntion. and then the orchestrator function will pick where it left and process the final output.</div><div>Once you enabled the Activity function, the Message saved in Azure Queue by Durable function is cleared as the Orchestrator function will process the final output.</div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-YCMDDLSJpz0/YJDhQYWu2PI/AAAAAAAAHwY/KOJ2ZdKCf_cWbcCeQR4jc1JRCDwdzulqQCLcBGAsYHQ/s2201/17.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="215" data-original-width="2201" height="62" src="https://1.bp.blogspot.com/-YCMDDLSJpz0/YJDhQYWu2PI/AAAAAAAAHwY/KOJ2ZdKCf_cWbcCeQR4jc1JRCDwdzulqQCLcBGAsYHQ/w640-h62/17.png" width="640" /></a></div><div><br /></div><b>Finally lets talk about Billing.</b><div>Unlike Azure functions, Durable-Functions were billed based on number of calls+ time of execution.</div><div>But, Orchestrator function has a special feature "Stop-Resume" behavior. Once one of the fucntion yeild the result, instead of waiting (synchronously), Orchestrator function will stop and saves its state as a message in a Queue. Once the response from second function arrives, it will start again and finishes its job.</div><div><br /></div><div>Hope i cclarified the concept of Durable-Functions.</div>Pratap Reddy Pilakahttp://www.blogger.com/profile/17141976956878163883noreply@blogger.com0tag:blogger.com,1999:blog-4208378482021219922.post-71530431126608070822021-04-30T17:50:00.003-07:002021-04-30T17:50:37.857-07:00Azure Functions Basics - Part 2<p>In earlier post, we have built the <b><span style="color: #6aa84f;">green</span></b> part of below app. <a href="https://pratapreddypilaka.blogspot.com/2021/04/azure-functions-basics-part-1.html" target="_blank">Part-1</a></p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-0bdNPtrxGQM/YIx6zMnYC3I/AAAAAAAAHsE/ix7SuDMj8dAd2zKLL8WagiAv-ho6LtANACLcBGAsYHQ/s840/2.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="524" data-original-width="840" height="400" src="https://1.bp.blogspot.com/-0bdNPtrxGQM/YIx6zMnYC3I/AAAAAAAAHsE/ix7SuDMj8dAd2zKLL8WagiAv-ho6LtANACLcBGAsYHQ/w640-h400/2.png" width="640" /></a></div><br /><p>Now lets continue building <b><span style="color: red;">red</span></b> part of the design.</p><a name='more'></a><p><b>Step 4:</b> Create Azure Queue</p><p>Go to Azure portal and search for "storage account". </p><p>If we refer to <b>Step1</b>, where we create our first Azure Function, there is a storage account created as part of it. Lets open and see it.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-Qlkg5ypLgQg/YIx9inuRfrI/AAAAAAAAHsM/pY4qnSUBmfgGrlTmFkf1mEnMNFZQBRvswCLcBGAsYHQ/s1223/3.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="453" data-original-width="1223" height="238" src="https://1.bp.blogspot.com/-Qlkg5ypLgQg/YIx9inuRfrI/AAAAAAAAHsM/pY4qnSUBmfgGrlTmFkf1mEnMNFZQBRvswCLcBGAsYHQ/w640-h238/3.png" width="640" /></a></div><div>Here you can see all the files related to Function App are stored in this blob storage.</div><div>Now lets create a Queue with name "orderqueue".</div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-URs7Eobjnwg/YIx_xZEnsJI/AAAAAAAAHsU/SN3IaiZNMTMNIa0ZBPHO09v8TBFgdSQDQCLcBGAsYHQ/s1112/4.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="472" data-original-width="1112" height="272" src="https://1.bp.blogspot.com/-URs7Eobjnwg/YIx_xZEnsJI/AAAAAAAAHsU/SN3IaiZNMTMNIa0ZBPHO09v8TBFgdSQDQCLcBGAsYHQ/w640-h272/4.png" width="640" /></a></div><br /><div><b>Step 5:</b> Update "Fn_Save_PurchaseOrder" function.</div><div>Open "Fn_Save_PurchaseOrder" function and go to "Integration" pane, add one more output. This time select it as Azure Queue storage. Give the detail and make sure you selected the Storageaccount in which we created the Queue and click ok.</div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-BvwgWL8IHSs/YIyBb3HdX3I/AAAAAAAAHsc/KpQXBHaSh-g3b_ByhGmTyl_xlLBPR3PowCLcBGAsYHQ/s2242/5.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="872" data-original-width="2242" height="248" src="https://1.bp.blogspot.com/-BvwgWL8IHSs/YIyBb3HdX3I/AAAAAAAAHsc/KpQXBHaSh-g3b_ByhGmTyl_xlLBPR3PowCLcBGAsYHQ/w640-h248/5.png" width="640" /></a></div>Now, the <b>"function.json"</b> file will be updated with new binding pertaining to Azure Queue.<div><br /></div><div>Switch to edit <b>"index.js" </b>file and update it with below code.</div><div><pre>module.exports = <span style="background-color: #fffffe; color: blue; font-family: Consolas, "Courier New", monospace; font-size: 14px;">async</span><span style="background-color: #fffffe; font-family: Consolas, "Courier New", monospace; font-size: 14px;"> </span><span style="background-color: #fffffe; color: blue; font-family: Consolas, "Courier New", monospace; font-size: 14px;">function</span><span style="background-color: #fffffe; font-family: Consolas, "Courier New", monospace; font-size: 14px;"> (context, req) {</span><div style="background-color: #fffffe; font-family: Consolas, "Courier New", monospace; font-size: 14px; line-height: 19px; white-space: pre;"><div> context.log(<span style="color: #a31515;">'JavaScript HTTP trigger function processed a request.'</span>);</div><br /><div> <span style="color: green;">//creating a JSON string from request data</span></div><div> <span style="color: blue;">var</span> newitem=<span style="color: teal;">JSON</span>.stringify({</div><div> <span style="color: #a31515;">"id"</span>: req.body.id,</div><div> <span style="color: #a31515;">"item"</span>:req.body.item,</div><div> <span style="color: #a31515;">"make"</span>:req.body.make,</div><div> <span style="color: #a31515;">"model"</span>:req.body.model,</div><div> <span style="color: #a31515;">"buyer"</span>:req.body.buyer,</div><div> <span style="color: #a31515;">"address"</span>:req.body.address</div><div> });</div><br /><div> <span style="color: green;">//saving the new item to newpurchaseorder output, </span></div><div> <span style="color: green;">//which is PurchaseOrders container in this case</span></div><div> context.bindings.newpurchaseorder=newitem;</div><br /><div> <span style="color: green;">//Save the order item to Azure queue</span></div><div> context.bindings.neworderqueueitem=newitem;</div><br /><div> <span style="color: green;">//confirming the save success</span></div><div> context.res={</div><div> status:<span style="color: #09885a;">200</span>,</div><div> body : <span style="color: #a31515;">"Purchase Order saved sucesfully."</span>,</div><div> headers: {</div><div> <span style="color: #a31515;">'Content-Type'</span>: <span style="color: #a31515;">'application/json'</span></div><div> }</div><div> }</div></div><span style="background-color: #fffffe; font-family: Consolas, "Courier New", monospace; font-size: 14px;">}</span></pre><br /><div>Now lets test the functionality, using the same input form before.</div><div><pre>{<div style="background-color: #fffffe; font-family: Consolas, "Courier New", monospace; font-size: 14px; line-height: 19px; white-space: pre;"><div> <span style="color: #a31515;">"id"</span>: <span style="color: #0451a5;">"1002"</span>,</div><div> <span style="color: #a31515;">"item"</span>:<span style="color: #0451a5;">"mobile"</span>,</div><div> <span style="color: #a31515;">"make"</span>:<span style="color: #0451a5;">"samsung"</span>,</div><div> <span style="color: #a31515;">"model"</span>:<span style="color: #0451a5;">"S21"</span>,</div><div> <span style="color: #a31515;">"buyer"</span>:<span style="color: #0451a5;">"Pratap"</span>,</div><div> <span style="color: #a31515;">"address"</span>:<span style="color: #0451a5;">"Wellington,NZ"</span></div></div><span style="background-color: #fffffe; font-family: Consolas, "Courier New", monospace; font-size: 14px;">}</span></pre></div><div>This time, it not only saves data to CosmosDB, but also adds a message ot Orderqueue.</div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-R11R3FmKSJY/YIyEOYA95pI/AAAAAAAAHsk/3D1oPibGE4sVGshDcy5jkKESAs7wPd9vwCLcBGAsYHQ/s1774/6.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="475" data-original-width="1774" height="172" src="https://1.bp.blogspot.com/-R11R3FmKSJY/YIyEOYA95pI/AAAAAAAAHsk/3D1oPibGE4sVGshDcy5jkKESAs7wPd9vwCLcBGAsYHQ/w640-h172/6.png" width="640" /></a></div><div><br /></div><b>Step 6:</b> Create more conatiners in our Cosmos DB to handle available stock data and shipping order data. I have added these two containers to same DB, and have a look at these partition keys.</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-CGe9zWdT1II/YIyF3bADL4I/AAAAAAAAHss/fIyPfqAlkyMuEIUgXXGF7fIBIASVB_IAgCLcBGAsYHQ/s1256/7.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="593" data-original-width="1256" height="302" src="https://1.bp.blogspot.com/-CGe9zWdT1II/YIyF3bADL4I/AAAAAAAAHss/fIyPfqAlkyMuEIUgXXGF7fIBIASVB_IAgCLcBGAsYHQ/w640-h302/7.png" width="640" /></a></div>I am adding some sample items to "AvailableStock".<div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-gZ0-_KVr42s/YIyZMcFN8BI/AAAAAAAAHtE/PlCF5Npn_DMZKmmeMpVjdDnesrJIlo_JgCLcBGAsYHQ/s1303/8.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="492" data-original-width="1303" height="242" src="https://1.bp.blogspot.com/-gZ0-_KVr42s/YIyZMcFN8BI/AAAAAAAAHtE/PlCF5Npn_DMZKmmeMpVjdDnesrJIlo_JgCLcBGAsYHQ/w640-h242/8.png" width="640" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><br /></div><div><b>Step 7:</b> Create a new Azure Function "Fn_ConfirmDelivery". Go to Function Apps, select "Purchase OrderDemo", add a new function. Rember Function App is a container of Functions, so you can have multiple functions defined in same Function App.</div><div><br /></div><div>This time choose "Azure Queue Storage trigger", as we want this function to be triggered when ever there is a new item added to "orderqueue". Give the details accordingly.</div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-F85GJnVg5sI/YIyJj1-t1WI/AAAAAAAAHs8/IY8KH9mjOUsmdnGoFehBcGAgtTaQhYWHgCLcBGAsYHQ/s1196/9.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1196" data-original-width="843" height="640" src="https://1.bp.blogspot.com/-F85GJnVg5sI/YIyJj1-t1WI/AAAAAAAAHs8/IY8KH9mjOUsmdnGoFehBcGAgtTaQhYWHgCLcBGAsYHQ/w453-h640/9.png" width="453" /></a></div><br /><div>Once the function is created, go to "Integration" pane. This function has to read information from "AvailableStock" container and confirm the delivery. So lets add a new input and new outputs.</div><div>Please note that input is from CosmosDB's <b>AvailableStock</b> container to read the avialbility and Output is to CosmosDB's <b>ShippingOrders</b> container for creating a new shipment.</div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-w6M-vl_Eso4/YIygb5VZYSI/AAAAAAAAHtM/kHvcdQYfCy8jwIFRKTf81R6h3O4b6XgrwCLcBGAsYHQ/s1059/10.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1059" data-original-width="998" height="640" src="https://1.bp.blogspot.com/-w6M-vl_Eso4/YIygb5VZYSI/AAAAAAAAHtM/kHvcdQYfCy8jwIFRKTf81R6h3O4b6XgrwCLcBGAsYHQ/w604-h640/10.png" width="604" /></a></div><br /><div>Now go to index.js file and update the code to below block.</div><div><br /></div><div><pre>module.exports = <span style="background-color: #fffffe; color: blue; font-family: Consolas, "Courier New", monospace; font-size: 14px;">async</span><span style="background-color: #fffffe; font-family: Consolas, "Courier New", monospace; font-size: 14px;"> </span><span style="background-color: #fffffe; color: blue; font-family: Consolas, "Courier New", monospace; font-size: 14px;">function</span><span style="background-color: #fffffe; font-family: Consolas, "Courier New", monospace; font-size: 14px;"> (context, myQueueItem) {</span><div style="background-color: #fffffe; font-family: Consolas, "Courier New", monospace; font-size: 14px; line-height: 19px; white-space: pre;"><div> context.log(<span style="color: #a31515;">'JavaScript queue trigger function processed work item'</span>, myQueueItem);</div><div> </div><div> <span style="color: blue;">var</span> avilablemodel=context.bindings.avilablestock.find(model => model = myQueueItem.model) </div><div> </div><div> <span style="color: blue;">if</span>(avilablemodel.availableunits><span style="color: #09885a;">0</span>)</div><div> {</div><div> <span style="color: blue;">var</span> newitemdelivery= <span style="color: teal;">JSON</span>.stringify({</div><div> <span style="color: #a31515;">"id"</span>:myQueueItem.buyer+<span style="color: #a31515;">"-"</span>+myQueueItem.model,</div><div> <span style="color: #a31515;">"shiporderid"</span>:myQueueItem.buyer+<span style="color: #a31515;">"-"</span>+myQueueItem.model,</div><div> <span style="color: #a31515;">"address"</span>:myQueueItem.address,</div><div> <span style="color: #a31515;">"item"</span>: avilablemodel.item+avilablemodel.model</div><div> })</div><br /><div> context.bindings.neworderdelivery=newitemdelivery</div><div> context.log(<span style="color: #a31515;">"Requested Item is ready for shipping"</span>)</div><div> }</div><div> </div></div><span style="background-color: #fffffe; font-family: Consolas, "Courier New", monospace; font-size: 14px;">};</span></pre></div><div>Save the function and test run by giving below data.</div><div><br /></div><div><div><pre>{<div style="background-color: #fffffe; font-family: Consolas, "Courier New", monospace; font-size: 14px; line-height: 19px;"><div> <span style="color: #a31515;">"id"</span>: <span style="color: #0451a5;">"1002"</span>,</div><div> <span style="color: #a31515;">"item"</span>:<span style="color: #0451a5;">"mobile"</span>,</div><div> <span style="color: #a31515;">"make"</span>:<span style="color: #0451a5;">"samsung"</span>,</div><div> <span style="color: #a31515;">"model"</span>:<span style="color: #0451a5;">"S21"</span>,</div><div> <span style="color: #a31515;">"buyer"</span>:<span style="color: #0451a5;">"Pratap"</span>,</div><div> <span style="color: #a31515;">"address"</span>:<span style="color: #0451a5;">"Wellington,NZ"</span></div></div><span style="background-color: #fffffe; font-family: Consolas, "Courier New", monospace; font-size: 14px;">}</span></pre></div></div><div><br /><div>This will read the item availability and if avialable, it will create a record in ShippingOrders as we expected.</div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-V-QQsoojm7M/YIyiXYft2AI/AAAAAAAAHtU/Lc8XC5voJtYdhKJPw9fpEBfZDDr9ldSCgCLcBGAsYHQ/s1554/11.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="803" data-original-width="1554" height="330" src="https://1.bp.blogspot.com/-V-QQsoojm7M/YIyiXYft2AI/AAAAAAAAHtU/Lc8XC5voJtYdhKJPw9fpEBfZDDr9ldSCgCLcBGAsYHQ/w640-h330/11.png" width="640" /></a></div><br /><div>Great. Since we finished everything from design, lets give a go from end to end.</div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-yp1-X6h4KTQ/YIyiqc-wEBI/AAAAAAAAHtc/ub4Hj9HE4CYdUbvkz1Loo7fIWgZvUSwzQCLcBGAsYHQ/s840/1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="524" data-original-width="840" height="400" src="https://1.bp.blogspot.com/-yp1-X6h4KTQ/YIyiqc-wEBI/AAAAAAAAHtc/ub4Hj9HE4CYdUbvkz1Loo7fIWgZvUSwzQCLcBGAsYHQ/w640-h400/1.png" width="640" /></a></div><br /><div>This starts with a Http invocation to first azure function, and that should add order details in first container, then add a new message to orderqueue. This should trigger second function, which checks for availability and logs a shipping order in third container.</div><div><br /></div><div>I am triggering the first function with below test data, a new watch for me.</div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-doYhuNi164w/YIykLZ6-R3I/AAAAAAAAHtk/E-peGanVnRIURXa3FABVaf7c0dIhkEWhgCLcBGAsYHQ/s2048/12.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1050" data-original-width="2048" height="328" src="https://1.bp.blogspot.com/-doYhuNi164w/YIykLZ6-R3I/AAAAAAAAHtk/E-peGanVnRIURXa3FABVaf7c0dIhkEWhgCLcBGAsYHQ/w640-h328/12.png" width="640" /></a></div><br /><div>Now lets go to CosmosDB and check both <b>"PurchaseOrders" , "ShipmentOrders"</b> containers for the results.</div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-xKkmMxM3-yE/YIylZ6y1bpI/AAAAAAAAHts/38z8ulFujjwQNZBtgkNA1Xgo9UzTBwCxQCLcBGAsYHQ/s1341/13.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="932" data-original-width="1341" height="444" src="https://1.bp.blogspot.com/-xKkmMxM3-yE/YIylZ6y1bpI/AAAAAAAAHts/38z8ulFujjwQNZBtgkNA1Xgo9UzTBwCxQCLcBGAsYHQ/w640-h444/13.png" width="640" /></a></div><br /><div><br /></div><div><br /><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><br /><p><br /></p></div></div>Pratap Reddy Pilakahttp://www.blogger.com/profile/17141976956878163883noreply@blogger.com0tag:blogger.com,1999:blog-4208378482021219922.post-84415582684141936902021-04-30T06:54:00.007-07:002021-04-30T17:54:45.989-07:00Azure Functions Basics - Part 1<p><b>Azu</b><span style="font-family: inherit;"><b>re Function:</b> </span><span style="background-color: white; color: #171717; font-size: 16px;"><span style="font-family: inherit;">Azure Functions is a serverless application platform. It allows developers to host business logic that can be executed without provisioning infrastructure. Functions provides intrinsic scalability and you are charged only for the resources used. You can write your function code in the language of your choice, including C#, F#, JavaScript, Python, and PowerShell Core. Support for package managers like NuGet and NPM is also included, so you can use popular libraries in your business logic.</span></span></p><p><span style="color: #4c4c51;"><span style="background-color: white;">Says Microsoft, now we will build some simple stuff shown below and understand basics while we do so.</span></span><a href="https://1.bp.blogspot.com/-dzw1cC_hZ3Q/YIvb6kWSN6I/AAAAAAAAHps/FZDVMNqf0807WUtBtvde9MTCVZDC7Ci-QCLcBGAsYHQ/s840/1.png" style="margin-left: 1em; margin-right: 1em; text-align: center;"><img border="0" data-original-height="524" data-original-width="840" height="400" src="https://1.bp.blogspot.com/-dzw1cC_hZ3Q/YIvb6kWSN6I/AAAAAAAAHps/FZDVMNqf0807WUtBtvde9MTCVZDC7Ci-QCLcBGAsYHQ/w640-h400/1.png" width="640" /></a></p><a name='more'></a><span style="color: #4c4c51;"><br /><span style="background-color: white;">Azure function is a peice of code exceuted by events called triggers. They are meant for short period exceutions and designed to reduce complexity of your business logic.</span></span><p></p><p><span style="color: #4c4c51;"><span style="background-color: white;">This triggers may be a http call (or) a timely repetetion (or) a new item added in a CosmosDB (or) a new message added to a Azure Queue storage (or) a new item uploaded to a blob storage. There are wide variety of triggers avilable, which we see going ahaead.</span></span></p><p><span style="color: #4c4c51;"><span style="background-color: white;">But, you should ask a question, "Why Azure function instead of an App Service?"</span></span></p><p><span style="color: #4c4c51;"><span style="background-color: white;">App service is used to host both web applications and APIs. So why dont we deploy our code in App Services?</span></span></p><p><span style="color: #4c4c51;"><span style="background-color: white;">App Services are PaaS, this means though you haven't deployed a dedicated server, App services will be hosted on bunch of servers managed by Microsoft. So you will be paying money for hosting, irrespective of the usage. </span></span><span style="background-color: white; color: #4c4c51;">Where as in Function Apps, you pay for the usage.</span></p><p><span style="color: #4c4c51;"><span style="background-color: white;">I am not saying Azure function is suitable for everything. </span></span></p><p><span style="color: #4c4c51;"><span style="background-color: white;">Here are some limitations:</span></span></p><p><span style="color: #4c4c51;"><span style="background-color: white;">1. <b>Exceution time 10 mins:</b> Not suitable for long running or complex business processes. Use WebJobs for long running process. If your code requires mutiple re-trys or communication with external resources try app services instead.</span></span></p><p><span style="color: #4c4c51;"><span style="background-color: white;">2. <b>Exceution Frequency:</b> Surely more Azure Function instances will be created every 10 seconds up to total 200 instances and each instance support many concurrent requests. Azure Functions are meant for short and on-demand calls. So if you have a functionality that should run 24/7 , better choose either App service or a service hosted on your dedictaed VM instance. </span></span></p><p><span style="color: #4c4c51;"><span style="background-color: white;"><b>Step 1: </b>Go to azure portal and serach for Function Apps.</span></span></p><p><span style="color: #4c4c51;"><span style="background-color: white;"><b>Azure Function App</b> is a container/wrapper for a set of functions.</span></span></p><p><span style="color: #4c4c51;"><span style="background-color: white;">Create a Function App. I choose Resource group as "SampleRG", Function Name as "PurchaseOrderDemo" and Language as "Node.JS".</span></span></p><p><span style="color: #4c4c51;"><span style="background-color: white;">Please note that a storageaccount will be created as part of this. We will use it in later steps.</span></span></p><p>Lets create a new function. You can go to "Functions" option on left pane and click it.<br /></p><p>Add a new function using "+Add" button on top.</p><p>You will be presented with a bunch of triggers. For our first function, choose "Http Trigger".</p><p>Once you select trigger, you can name the function and select authorization. </p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-MSFREWl4bY4/YIvsp7CPSvI/AAAAAAAAHqM/pezw0id0AO41hAG_-GEM496PFOyU35-NQCLcBGAsYHQ/s830/2.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="417" data-original-width="830" height="201" src="https://1.bp.blogspot.com/-MSFREWl4bY4/YIvsp7CPSvI/AAAAAAAAHqM/pezw0id0AO41hAG_-GEM496PFOyU35-NQCLcBGAsYHQ/w400-h201/2.png" width="400" /></a></div><br /><p>If you select "Fucntion" as authorization, you need a key to invoke this function, which i will show in next step. Go ahead and create the function. Once the function is created , you will be taken to a page like this.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-KP7ZT1Yv8LM/YIvtxrWawLI/AAAAAAAAHqU/a11v3kH2-2UeJ7iFP94r-IvbWztZhffGwCLcBGAsYHQ/s516/3.png" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="467" data-original-width="516" src="https://1.bp.blogspot.com/-KP7ZT1Yv8LM/YIvtxrWawLI/AAAAAAAAHqU/a11v3kH2-2UeJ7iFP94r-IvbWztZhffGwCLcBGAsYHQ/s320/3.png" width="320" /></a></div><div><br /></div><b>"Code+Test"</b> is where you will write your code and do unit testing.<br /><p><br /></p><p></p><p><span style="color: #4c4c51;"><span style="background-color: white;"><b>"Integration"</b> is visual tool where we will add new inputs like reading data from other components like Queues and CosmosDB. </span></span></p><p><span style="color: #4c4c51;"><span style="background-color: white;">By default the function will be created with some sample code which we can exceute and see.</span></span></p><p><span style="color: #4c4c51;"><span style="background-color: white;">Lets see how it goes.</span></span></p><p><span style="color: #4c4c51;"><span style="background-color: white;"><br /></span></span></p><p><span style="color: #4c4c51;"><span style="background-color: white;"><br /></span></span></p><p><span style="color: #4c4c51;"></span></p><div class="separator" style="clear: both; text-align: center;"><span style="color: #4c4c51;"><a href="https://1.bp.blogspot.com/-uVTrSFXuy4M/YIvv9oU9_tI/AAAAAAAAHqc/x4fwB3zcezUauxSjWrRc5YLefUsGs6ofwCLcBGAsYHQ/s2235/4.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="721" data-original-width="2235" height="206" src="https://1.bp.blogspot.com/-uVTrSFXuy4M/YIvv9oU9_tI/AAAAAAAAHqc/x4fwB3zcezUauxSjWrRc5YLefUsGs6ofwCLcBGAsYHQ/w640-h206/4.png" width="640" /></a></span></div><span style="color: #4c4c51;"><br /><span style="background-color: white;">First understand the code. All it does is accept a Http request and fetch the "name" key-value pair from its body and write a response saying Hello to the name value we provided. </span></span><p></p><p><span style="color: #4c4c51;"><span style="background-color: white;">There will be 2 files associated to the function. </span></span></p><p><span style="color: #4c4c51;"><span style="background-color: white;"><b>"Index.js"</b> file will have all our code. </span></span></p><p><span style="color: #4c4c51;"><span style="background-color: white;"><b>"function.json"</b> file will have binding information. This code will be geneated from the triggers and inputs we configure in "Integration" part. You see more when we configure inputs for CosmosDB and Azure Queues.</span></span></p><p><span style="color: #4c4c51;"><span style="background-color: white;">Now click on that "Get Function URL" button and paste it in a new tab. It will be something like this.</span></span></p><p><span style="color: #4c4c51;"></span></p><div class="separator" style="clear: both; text-align: center;"><span style="color: #4c4c51;"><a href="https://1.bp.blogspot.com/-8p1OvTsNKpI/YIvxTPw5UPI/AAAAAAAAHqk/XQubvkA64fARPV5GcUW_cHgEpP196O-xwCLcBGAsYHQ/s1739/5.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="192" data-original-width="1739" height="70" src="https://1.bp.blogspot.com/-8p1OvTsNKpI/YIvxTPw5UPI/AAAAAAAAHqk/XQubvkA64fARPV5GcUW_cHgEpP196O-xwCLcBGAsYHQ/w640-h70/5.png" width="640" /></a></span></div><span style="color: #4c4c51;">Look at the highlighted part in URL, this is what you copied from the "Get Function URL" button. There is a code embeded there due to the fact we choose "Function" as authorization earlier. If you choose "Anonymous" as authorization, you need no code to invoke this function via http call.</span><p></p><p><span style="color: #4c4c51;">I have added "&name=PRATAP" at the end, this is the body of the http call i passed as query string.</span></p><p><span style="color: #4c4c51;">Thus by making a http call i invoked the function, and it responded to me by reading my name from the call.</span></p><p><span style="color: #4c4c51;">This is what out of the box functionality does.</span></p><p><span style="color: #4c4c51;"><b>Step 2: </b>Time to create a Cosmos db and containers for our purchase order data . A database account is required to create DB. </span></p><p><span style="color: #4c4c51;"></span></p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-KkTswRct0aw/YIv2KTrEsPI/AAAAAAAAHqs/Lr-Iu6P2ND8Apv0T624M0MTcU5Nucy_ogCLcBGAsYHQ/s943/6.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="660" data-original-width="943" height="280" src="https://1.bp.blogspot.com/-KkTswRct0aw/YIv2KTrEsPI/AAAAAAAAHqs/Lr-Iu6P2ND8Apv0T624M0MTcU5Nucy_ogCLcBGAsYHQ/w400-h280/6.png" width="400" /></a></div><br /> Once DB account it created you will be taken to a page, on the left pannel click "Data Explorer" option. It will be empty. <p></p><p><span style="color: #4c4c51;">Lets create the Cosmos DB now. If you click the arrow next to "+New Container" , you can see <b>"+New Database" </b>option. Select it.</span></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-PuDRUe5dRXM/YIv3WxgAZCI/AAAAAAAAHq0/6xCBaqDMzrQ2hiupPLkFiQIYIIEFoZLNACLcBGAsYHQ/s657/7.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="598" data-original-width="657" height="364" src="https://1.bp.blogspot.com/-PuDRUe5dRXM/YIv3WxgAZCI/AAAAAAAAHq0/6xCBaqDMzrQ2hiupPLkFiQIYIIEFoZLNACLcBGAsYHQ/w400-h364/7.png" width="400" /></a></div><br /><span style="color: #4c4c51;">Once the DB is created, this is how Data Explorer will look like.</span><p></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-p75gsLwrCN4/YIv4AdaHOvI/AAAAAAAAHq8/HPTI4AGljjQw-fmC7ikBycAmeqyIrUXpACLcBGAsYHQ/s953/8.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="424" data-original-width="953" height="178" src="https://1.bp.blogspot.com/-p75gsLwrCN4/YIv4AdaHOvI/AAAAAAAAHq8/HPTI4AGljjQw-fmC7ikBycAmeqyIrUXpACLcBGAsYHQ/w400-h178/8.png" width="400" /></a></div><br /><span style="color: #4c4c51;">Now select <b>"New Container"</b> option. A cointainer is collection of Items. I choose "/id" as partition key.</span><p></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-tjYmjZO5vI8/YIv6m2wJTMI/AAAAAAAAHrM/T1DsWjnaa-o0LivohW4oxBP37Rt_m6iiQCLcBGAsYHQ/s623/9.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="623" data-original-width="621" height="400" src="https://1.bp.blogspot.com/-tjYmjZO5vI8/YIv6m2wJTMI/AAAAAAAAHrM/T1DsWjnaa-o0LivohW4oxBP37Rt_m6iiQCLcBGAsYHQ/w399-h400/9.png" width="399" /></a></div><br /><span style="color: #4c4c51;">Once container is created, lets test it by adding a item manually.</span><p></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-iCIlDj48Z9U/YIv7hpFpqmI/AAAAAAAAHrU/1cqmdC17RkUy9dX0IkbZ9ASveu50CP-lwCLcBGAsYHQ/s1762/10.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="632" data-original-width="1762" height="230" src="https://1.bp.blogspot.com/-iCIlDj48Z9U/YIv7hpFpqmI/AAAAAAAAHrU/1cqmdC17RkUy9dX0IkbZ9ASveu50CP-lwCLcBGAsYHQ/w640-h230/10.png" width="640" /></a></div><br /><span style="color: #4c4c51;">Remeber Cosmos DB is a NoSQL database designed to store non sturctured data like json. </span><p></p><p><span style="color: #4c4c51;">Click on "New Item" button and enter our first data item record, and click "Save" button.</span></p><p><span style="color: #4c4c51;">BOOM... new item information has been saved to Cosmos DB. Lets modify our <b>"Fn_Save_PurchaseOrder"</b> function to do the same, when it was invoked with right data.</span></p><p><b>Step 3: </b>Open the function we have created and tested earlier "Fn_Save_PurchaseOrder".</p><p>Click on <b>"Integration"</b> from left menu. A visual flow will be displayed showing the triggers , inputs and outputs. We created this function to be a Http trigger, so that out mobile app can send a http call to trigger save operation. </p><p>Saving the purchase order information to CosmosDB should be the output of this function.</p><p>Click on the <b>"+ Add Output"</b> button and select "CosmosDB" and give the DB name, Collection name.</p><p> Now look at the <b>"Document parameter name"</b> field. The value we give in this field is registered in "function.json" file.</p><p>Now look at <b>"Cosmos DB account name connection"</b> field. Intially it will be empty. By clicking <b>"New"</b> button, you get to select the database account with which this DB is connected to. </p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-lcsqnZyOXP0/YIwBEN9botI/AAAAAAAAHrc/mqwENCLBS_M0DejT_qLfXVRtZWb-t3DfQCLcBGAsYHQ/s2048/11.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1063" data-original-width="2048" height="332" src="https://1.bp.blogspot.com/-lcsqnZyOXP0/YIwBEN9botI/AAAAAAAAHrc/mqwENCLBS_M0DejT_qLfXVRtZWb-t3DfQCLcBGAsYHQ/w640-h332/11.png" width="640" /></a></div><br /><p>Click "OK" and save it. Earlier i talked about "function.json" file. The code inside holds all the refrences to triggers and inouts and outputs. This is how it looks right now.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-SyhOZegk4Yg/YIwBx19_IOI/AAAAAAAAHrk/KnGxzgHOVckFfwirwGIyPaZL-7FN5r7SwCLcBGAsYHQ/s1324/12.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="957" data-original-width="1324" height="462" src="https://1.bp.blogspot.com/-SyhOZegk4Yg/YIwBx19_IOI/AAAAAAAAHrk/KnGxzgHOVckFfwirwGIyPaZL-7FN5r7SwCLcBGAsYHQ/w640-h462/12.png" width="640" /></a></div><br /><p>Now select index.js file and paste below code.</p><p></p><pre>module.exports = <span style="background-color: #fffffe; color: blue; font-family: Consolas, "Courier New", monospace; font-size: 14px;">async</span><span style="background-color: #fffffe; font-family: Consolas, "Courier New", monospace; font-size: 14px;"> </span><span style="background-color: #fffffe; color: blue; font-family: Consolas, "Courier New", monospace; font-size: 14px;">function</span><span style="background-color: #fffffe; font-family: Consolas, "Courier New", monospace; font-size: 14px;"> (context, req) {</span><div style="background-color: #fffffe; font-family: Consolas, "Courier New", monospace; font-size: 14px; line-height: 19px; white-space: pre;"><div> context.log(<span style="color: #a31515;">'JavaScript HTTP trigger function processed a request.'</span>);</div><br /><div> <span style="color: green;">//creating a JSON string from request data</span></div><div> <span style="color: blue;">var</span> newitem=<span style="color: teal;">JSON</span>.stringify({</div><div> <span style="color: #a31515;">"id"</span>: req.body.id,</div><div> <span style="color: #a31515;">"item"</span>:req.body.item,</div><div> <span style="color: #a31515;">"make"</span>:req.body.make,</div><div> <span style="color: #a31515;">"model"</span>:req.body.model,</div><div> <span style="color: #a31515;">"buyer"</span>:req.body.buyer,</div><div> <span style="color: #a31515;">"address"</span>:req.body.address</div><div> });</div><br /><div> <span style="color: green;">//saving the new item to newpurchaseorder output, </span></div><div> <span style="color: green;">//which is PurchaseOrders container in this case</span></div><div> context.bindings.newpurchaseorder=newitem;</div><br /><div> <span style="color: green;">//confirming the save success</span></div><div> context.res={</div><div> status:<span style="color: #09885a;">200</span>,</div><div> body : <span style="color: #a31515;">"Purchase Order saved sucesfully."</span>,</div><div> headers: {</div><div> <span style="color: #a31515;">'Content-Type'</span>: <span style="color: #a31515;">'application/json'</span></div><div> }</div><div> }</div></div><span style="background-color: #fffffe; font-family: Consolas, "Courier New", monospace; font-size: 14px;">}</span></pre><p></p><p><span style="color: #4c4c51;">Now save <b>"Index.js"</b> file. Click <b>"Test/Run"</b> button to unti test our code.</span></p><p><span style="color: #4c4c51;">Use below code in the body for unti testing.</span></p><p><span style="color: #4c4c51;"></span></p><pre>{<div style="background-color: #fffffe; font-family: Consolas, "Courier New", monospace; font-size: 14px; line-height: 19px; white-space: pre;"><div> <span style="color: #a31515;">"id"</span>: <span style="color: #0451a5;">"1002"</span>,</div><div> <span style="color: #a31515;">"item"</span>:<span style="color: #0451a5;">"mobile"</span>,</div><div> <span style="color: #a31515;">"make"</span>:<span style="color: #0451a5;">"samsung"</span>,</div><div> <span style="color: #a31515;">"model"</span>:<span style="color: #0451a5;">"S21"</span>,</div><div> <span style="color: #a31515;">"buyer"</span>:<span style="color: #0451a5;">"Pratap"</span>,</div><div> <span style="color: #a31515;">"address"</span>:<span style="color: #0451a5;">"Wellington,NZ"</span></div></div><span style="color: #4c4c51;">}</span></pre><span style="color: #4c4c51;">Select the post method and run the test.</span><div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-N2ihfIiChzY/YIwHJCcBELI/AAAAAAAAHrs/n8XZce4a0z0SVs05pdzIDdOkfJi9BzIXgCLcBGAsYHQ/s830/13.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="509" data-original-width="830" height="245" src="https://1.bp.blogspot.com/-N2ihfIiChzY/YIwHJCcBELI/AAAAAAAAHrs/n8XZce4a0z0SVs05pdzIDdOkfJi9BzIXgCLcBGAsYHQ/w400-h245/13.png" width="400" /></a></div><br /><span style="color: #4c4c51;">Lets check the data in CosmosDB as well.</span></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-werZMvgfoA0/YIwHkAmApOI/AAAAAAAAHr0/JnLx7tk9cp4abeI_cIfWr85_KZqXQZP7QCLcBGAsYHQ/s1705/14.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="620" data-original-width="1705" height="232" src="https://1.bp.blogspot.com/-werZMvgfoA0/YIwHkAmApOI/AAAAAAAAHr0/JnLx7tk9cp4abeI_cIfWr85_KZqXQZP7QCLcBGAsYHQ/w640-h232/14.png" width="640" /></a></div><br /><span style="color: #4c4c51;">There it is. Data saved to DB sucessfully.</span></div><div><span style="color: #4c4c51;"><br /></span></div><div><span style="color: #4c4c51;">Now looking at the design again, we should ask why do we need second Azure function to confirm deliveries. Cant we just add that code in this function.</span></div><div><span style="color: #4c4c51;"><br /></span></div><div><span style="color: #4c4c51;">Answer: What if there is an exception generated in confirmation part, How do we handle it?</span></div><div><span style="color: #4c4c51;">Azure functions are good at doing one thing "DOING ONE THING". </span></div><div><span style="color: #4c4c51;"><br /></span></div><div><span style="color: #4c4c51;">In this design we add a message to a Azure Queue, which will trigger another function, which checks avilability of items and create items in Ordershipments. </span></div><div><span style="color: #4c4c51;"><br /></span></div><div><span style="color: #4c4c51;">Lets say if the items are not available, then we add one more queue followed by another function which add the order details in Pending Orders container. </span></div><div><span style="color: #4c4c51;"><br /></span></div><div><span style="color: #4c4c51;">Usually people say there is no re-try capability in Azure functions, but by adding error handling and Azure Queues, we can even implement re-try functionality as well. </span></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-1RL_AUA-z7A/YIwJMajFUCI/AAAAAAAAHr8/fCnEjqkXm_ImgdCMURbenUdO1_SezT8gQCLcBGAsYHQ/s840/1.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="524" data-original-width="840" height="400" src="https://1.bp.blogspot.com/-1RL_AUA-z7A/YIwJMajFUCI/AAAAAAAAHr8/fCnEjqkXm_ImgdCMURbenUdO1_SezT8gQCLcBGAsYHQ/w640-h400/1.png" width="640" /></a></div> In the next article we will continue adding remining functionality which familiarizes us with hadling Azure Queues and both input and Output bindings of Azure Functions.</div><div><br /></div><div>Hope i made it as simple as possible. <a href="https://pratapreddypilaka.blogspot.com/2021/04/azure-functions-basics-part-2.html" target="_blank">Part-2 link is here.</a></div><div><p></p></div>Pratap Reddy Pilakahttp://www.blogger.com/profile/17141976956878163883noreply@blogger.com0tag:blogger.com,1999:blog-4208378482021219922.post-27782690841893171222021-04-25T06:57:00.001-07:002021-04-25T06:57:25.522-07:00Azure Policy and Compliance Management<p><span style="background-color: white; color: #171717; font-size: 16px;"><span style="font-family: inherit;">Azure Policy helps to enforce organizational standards and to assess compliance at-scale. Through its compliance dashboard, it provides an aggregated view to evaluate the overall state of the environment, with the ability to drill down to the per-resource, per-policy granularity.</span></span></p><p><span style="background-color: white; color: #171717; font-size: 16px;"><span style="font-family: inherit;">Thats what Microsoft documentation says. Now let me say in my way.</span></span></p><p><span style="background-color: white; color: #171717; font-size: 16px;"><span style="font-family: inherit;">I work in a bank and the regulatory compliance says, data of our bank shouldn't leave Autralia.</span></span></p><p><span style="background-color: white; color: #171717; font-size: 16px;"><span style="font-family: inherit;">So if one of our developers deployed a production azure resource in any other AZ Regions, our organization has to pay big penalties for not complying with regulations.</span></span></p><p><span style="background-color: white; color: #171717; font-size: 16px;"><span style="font-family: inherit;">How do we do it? Using Azure Policy Assignment.</span></span></p><p><span style="background-color: white; color: #171717; font-size: 16px;">Before we jump on to implemntation, you need to know about difference between Policy and Initiative.</span></p><p><span style="background-color: white; color: #171717; font-size: 16px;">To keep it simple, Policy is a rule and Initiave is a collcetion of policies which set a standard.</span></p><p><span style="background-color: white; color: #171717; font-size: 16px;">Eg: ISO(standard) is a Intiative, which comprises of many policies which makes that standard.</span></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-vmaGQterBQs/YIVe241mX4I/AAAAAAAAHoM/soCsPOM8-LAGcOk7Sj4H-Ayf8lW-lt7WACLcBGAsYHQ/s1662/3.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="678" data-original-width="1662" height="262" src="https://1.bp.blogspot.com/-vmaGQterBQs/YIVe241mX4I/AAAAAAAAHoM/soCsPOM8-LAGcOk7Sj4H-Ayf8lW-lt7WACLcBGAsYHQ/w640-h262/3.png" width="640" /></a></div><span style="background-color: white; color: #171717; font-size: 16px;"><a name='more'></a><p>So you can assign either a policy or an initiative to a resource group or entire subscription to make your resources comply with regulations. You can create new policies or initiatives of your own that were tailored for your organizational needs.</p></span><p></p><p><span style="background-color: white; color: #171717; font-size: 16px;">Enough theory, lets implement.</span></p><p><span style="color: #171717;"><span style="background-color: white;">I have created a resource group "DefaultRG". So as per my Organizational needs, i need to make sure any resources created in this resource group are located in same region "Australia-East".</span></span></p><p><span style="color: #171717;"></span></p><div class="separator" style="clear: both; text-align: center;"><span style="color: #171717;"><a href="https://1.bp.blogspot.com/-HVX1XS8iTHQ/YIVfr7Hs23I/AAAAAAAAHoU/yf9SsV-UpfsJTnwzo3ey-tl-vhI_ObOOwCLcBGAsYHQ/s1002/1.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="419" data-original-width="1002" height="268" src="https://1.bp.blogspot.com/-HVX1XS8iTHQ/YIVfr7Hs23I/AAAAAAAAHoU/yf9SsV-UpfsJTnwzo3ey-tl-vhI_ObOOwCLcBGAsYHQ/w640-h268/1.png" width="640" /></a></span></div><span style="color: #171717;"><br /><span style="background-color: white;">Go to Azure portal and search for "Policy". </span></span><p></p><p><span style="color: #171717;"><span style="background-color: white;">There are many policies that were already defined in azure which we can use in our case. Click on Assignments, where we will assign an existing policy to our resource group.</span></span></p><p><span style="color: #171717;"></span></p><div class="separator" style="clear: both; text-align: center;"><span style="color: #171717;"><a href="https://1.bp.blogspot.com/-zh6lTQLliVU/YIVgo4lXbeI/AAAAAAAAHoc/bvpsKjZbjz4Uko6ivuitrPSRnC2LA_OaQCLcBGAsYHQ/s1087/2.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="741" data-original-width="1087" height="436" src="https://1.bp.blogspot.com/-zh6lTQLliVU/YIVgo4lXbeI/AAAAAAAAHoc/bvpsKjZbjz4Uko6ivuitrPSRnC2LA_OaQCLcBGAsYHQ/w640-h436/2.png" width="640" /></a></span></div><span style="color: #171717;"><br />In Policy assignment screen, i choose to apply this policy to a resource group, instead of entire subscription.</span><p></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-S658fqcaYjw/YIVj4IyX3pI/AAAAAAAAHok/9qFRlcg3A_QDGvufkfourixT7qfdNs8lgCLcBGAsYHQ/s2048/4.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1173" data-original-width="2048" height="366" src="https://1.bp.blogspot.com/-S658fqcaYjw/YIVj4IyX3pI/AAAAAAAAHok/9qFRlcg3A_QDGvufkfourixT7qfdNs8lgCLcBGAsYHQ/w640-h366/4.png" width="640" /></a></div><br /><span style="color: #171717;">In the "Parameters" and "Non Compliance Message" tabs, select the region you choose to deploy for your compliance and give the error message to display if someone try to defy the policy.</span><p></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-nI6MKOO3LJk/YIVlSbqZr6I/AAAAAAAAHos/_L4uCUz4ZLUyjYzIWpMoUSr-nAk0Ejy6gCLcBGAsYHQ/s1173/5.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="780" data-original-width="1173" height="426" src="https://1.bp.blogspot.com/-nI6MKOO3LJk/YIVlSbqZr6I/AAAAAAAAHos/_L4uCUz4ZLUyjYzIWpMoUSr-nAk0Ejy6gCLcBGAsYHQ/w640-h426/5.png" width="640" /></a></div><br /><span style="color: #171717;">Review and Create. </span><p></p><p><span style="color: #171717;">Since the policy is in place , lets test it.</span></p><p><span style="color: #171717;">I am using a Azure CLI command in powershell to create a VM in association with DefaultRG, but in a different region (Europe-West).</span></p><p><span style="color: #171717;">az </span></p><pre><span style="color: #171717;">vm create --resource-group DefaultRG --name myTestVM --location westeurope --image ubuntults --admin-username azureuser --admin-password AZpassword12345</span></pre><p></p><p><span style="color: #171717;">Now, lets see what happens. </span></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-sBx3dIRsRug/YIVqRtHvF0I/AAAAAAAAHo0/I3LuBKrBGoQ_dbsKW4dnDchgx3_VxumCgCLcBGAsYHQ/s1348/6.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="766" data-original-width="1348" height="364" src="https://1.bp.blogspot.com/-sBx3dIRsRug/YIVqRtHvF0I/AAAAAAAAHo0/I3LuBKrBGoQ_dbsKW4dnDchgx3_VxumCgCLcBGAsYHQ/w640-h364/6.png" width="640" /></a></div><br /><span style="color: #171717;">"</span><span style="color: #171717;">Error Code: RequestDisallowedByPolicy</span><p></p><p><span style="color: #171717;"> Message: Resource 'myTestVMVNET' was disallowed by policy. Reasons: 'Resources in DefaultRG should only be deployed to Australia-East region.'. See error details for policy resource IDs."</span></p><p><span style="color: #171717;">"MyTestLocationPolicy" assignment stopped me from creating a resource in a different region.</span></p><p><span style="color: #171717;">What happens if we applying a policy to an existing resources.</span></p><p><span style="color: #171717;">This time, i deleted the policy assignment and then created a new VM by keeping region as "Australia-East". </span></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-7We9vNL-724/YIVuYq6z-zI/AAAAAAAAHo8/JNCkKedREcYqAp4OuhuIrqy3MF0dKEYLgCLcBGAsYHQ/s1321/7.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="446" data-original-width="1321" height="216" src="https://1.bp.blogspot.com/-7We9vNL-724/YIVuYq6z-zI/AAAAAAAAHo8/JNCkKedREcYqAp4OuhuIrqy3MF0dKEYLgCLcBGAsYHQ/w640-h216/7.png" width="640" /></a></div><br /><span style="color: #171717;">Now i will add a new data disk under same resource group but the location as "Europe-West".</span><p></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-0cPyhWe9DsU/YIVu3B-3CfI/AAAAAAAAHpE/qZQI7XOm0PAfjqJsf92mEc0bxHFyJ-KUACLcBGAsYHQ/s1122/8.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="534" data-original-width="1122" height="304" src="https://1.bp.blogspot.com/-0cPyhWe9DsU/YIVu3B-3CfI/AAAAAAAAHpE/qZQI7XOm0PAfjqJsf92mEc0bxHFyJ-KUACLcBGAsYHQ/w640-h304/8.png" width="640" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-_7k-eWYHycQ/YIVvbHvMgkI/AAAAAAAAHpM/9NCG1BxBjNEzl_gXmLZfEiMqC0PvpslFgCLcBGAsYHQ/s1727/9.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="965" data-original-width="1727" height="358" src="https://1.bp.blogspot.com/-_7k-eWYHycQ/YIVvbHvMgkI/AAAAAAAAHpM/9NCG1BxBjNEzl_gXmLZfEiMqC0PvpslFgCLcBGAsYHQ/w640-h358/9.png" width="640" /></a></div><span style="color: #171717;"><p><span style="color: #171717;"><br /></span></p>Lets create the Policy assignment just like how we did earlier. After few minutes, the compliance status will be updated and it will show the non-compliance. </span><p></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-9BnLpFIDJ8o/YIVzJqEvk-I/AAAAAAAAHpU/RzAXXTxFzOsVRD2EWoeHVh8ugkfIbXXRQCLcBGAsYHQ/s2220/10.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="736" data-original-width="2220" height="212" src="https://1.bp.blogspot.com/-9BnLpFIDJ8o/YIVzJqEvk-I/AAAAAAAAHpU/RzAXXTxFzOsVRD2EWoeHVh8ugkfIbXXRQCLcBGAsYHQ/w640-h212/10.png" width="640" /></a></div><br /><span style="color: #171717;">Note that, if a new policy is applied and if there is a no-compliant resource , it will not do anything, except riase non-compliance notification.</span><p></p><p><span style="color: #171717;">Hope i bring some clarity on Azure Policies and Compliance Management. </span></p>Pratap Reddy Pilakahttp://www.blogger.com/profile/17141976956878163883noreply@blogger.com0tag:blogger.com,1999:blog-4208378482021219922.post-45525531517588629952020-08-17T17:43:00.006-07:002020-08-17T17:48:21.806-07:00Adding Swagger to Existing .Net Core WEB API Project<p>In this article we will see How we can add Swagger to an existing WebAPI project.</p><p><span style="font-size: x-large;">First why we need swagger here in this case?</span></p><p>Unlike SAOP based web sevrices or WCF Services, WebAPI are RESTFul. That means there is no WSDL in WebAPI. So Unless you prepare proper documentation about the methods, parameters and outputs, its hard to any consumer of the service to interpret them.</p><p>This is where swagger comes into the picture. If you use swagger and SwaggerUI in your WebAPI project, it pretty much takes care of the documentation part. It not only gives you the complete details of methods, parameters and outputs of the sevice, but also provides a UI component where consumers can test the service, without any third party tools.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-KGIIRIlI0k8/XzsSJqyLkKI/AAAAAAAAHe0/ENbDGGLiugU-bLiU8yHm-b3GTAwFVN7NwCLcBGAsYHQ/s853/1.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="426" data-original-width="853" src="https://1.bp.blogspot.com/-KGIIRIlI0k8/XzsSJqyLkKI/AAAAAAAAHe0/ENbDGGLiugU-bLiU8yHm-b3GTAwFVN7NwCLcBGAsYHQ/s640/1.png" width="640" /></a></div><a name='more'></a><p>For this example, i am using WebAPI project from one of the course on Linkedin Learning.</p><p>Here is the <a href="https://1drv.ms/u/s!AmYiNWkD2PZWn0Ha_FUdCDk3lb3S?e=96QQf8" target="_blank">link for you to download and try it for yourself</a>.</p><p>Download it and open the project and run it.</p><p>You will see this. With out any documentation, how anyone could interpret this.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-boFu97NH7ds/XzsUkHbagBI/AAAAAAAAHfA/4rdPyBCNsCodjvNkO-IjMqikfkOOat22wCLcBGAsYHQ/s667/2.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="363" data-original-width="667" src="https://1.bp.blogspot.com/-boFu97NH7ds/XzsUkHbagBI/AAAAAAAAHfA/4rdPyBCNsCodjvNkO-IjMqikfkOOat22wCLcBGAsYHQ/s640/2.png" width="640" /></a></div><p>Lets start with the swagg.</p><p><span style="font-size: x-large;"><b>Step 1:</b></span> Open Existing WebAPI project in Visual studio. Go to Tools and open "Nuget Package Manager" and click on "Manage Nuget Packages for the Solution".</p><p>Now browse for "Swashbuckle.ASP.Net Core", install it by selecting the WebAPI project.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-ENGbHxGBFLY/XzsW3Q6MyDI/AAAAAAAAHfM/BHh5EsIozgITLbN3EmCAElE-dn3C-mtIwCLcBGAsYHQ/s844/3.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="486" data-original-width="844" src="https://1.bp.blogspot.com/-ENGbHxGBFLY/XzsW3Q6MyDI/AAAAAAAAHfM/BHh5EsIozgITLbN3EmCAElE-dn3C-mtIwCLcBGAsYHQ/s640/3.png" width="640" /></a></div><p>This will install both Swagger and Swagger UI refreneces in your project.</p><p><span style="font-size: x-large;"><b>Step2:</b></span> Open Startup.cs file. This file handles all the regsitration of the controllers we use in WebAPI and the request pipelines. If you read default comments provided by framework itself, you will understand. </p><p>There are 2 methods in that file.</p><p>1. ConfigureServices(): This is where you need to register all the controllers you expose in your service.</p><p>2. Configure(): This is where you will register request pipeline like handlers,authorization and routing.</p><p>Add assembly reference of "<b>Microsoft.OpenAPI.Models</b>" </p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-fuOoVm11uMY/XzshOOFDdyI/AAAAAAAAHfY/9cMGtua1E1w6oofX_gsGxu91S4cuhuIPgCLcBGAsYHQ/s277/4.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="42" data-original-width="277" src="https://1.bp.blogspot.com/-fuOoVm11uMY/XzshOOFDdyI/AAAAAAAAHfY/9cMGtua1E1w6oofX_gsGxu91S4cuhuIPgCLcBGAsYHQ/s0/4.png" /></a></div><p>Add below code in ConfigureServices() method.</p><p></p><pre>services.AddSwaggerGen(c=> {
c.SwaggerDoc("v1", new OpenApiInfo {
Title="HPlus Sports",
Version="1.0",
Description="Product API for HPlus Sports."
});
});</pre><p></p><p> Add below code in Configure() method.</p><p></p><pre>app.UseSwagger();
app.UseSwaggerUI(c=> {
c.SwaggerEndpoint("/swagger/v1/swagger.json", "HPlus Sports");
});</pre><p></p><p>With this we requested to generate SwaggerDoc and also regietred it with endpoint "/swagger/v1/swagger.json".</p><p>Also we requested SwaggerUI to use the above endpoint for its testing purpose.</p><p><span style="font-size: x-large;"><b>Step 3:</b></span> Lets build the solution and test it.</p><p>Run the project and you will see this as earlier.</p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-vwWdpa78TpU/XzsiA1tRW0I/AAAAAAAAHfg/-NOTD5SSFM0jDOBu4iBC2K16PXQ230u2ACLcBGAsYHQ/s667/2.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="363" data-original-width="667" src="https://1.bp.blogspot.com/-vwWdpa78TpU/XzsiA1tRW0I/AAAAAAAAHfg/-NOTD5SSFM0jDOBu4iBC2K16PXQ230u2ACLcBGAsYHQ/s640/2.png" width="640" /></a></div> <p></p><p>Now change the url form "<a href="https://localhost:44321/api/product">https://localhost:44321/api/product</a>" to "<a href="https://localhost:44321/swagger/v1/swagger.json">https://localhost:44321/swagger/v1/swagger.json</a>" and you see the json of the service revealing all the details required for invoking methods with proper parameters.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-slmz56tRIAQ/XzsjCF7y9lI/AAAAAAAAHfs/1tCugp5VjCgUa3ldmAHpWnhG2scimZFKgCLcBGAsYHQ/s781/5.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="781" data-original-width="526" height="640" src="https://1.bp.blogspot.com/-slmz56tRIAQ/XzsjCF7y9lI/AAAAAAAAHfs/1tCugp5VjCgUa3ldmAHpWnhG2scimZFKgCLcBGAsYHQ/s640/5.png" /></a></div><p>On top of this if you change the url to "<a href="https://localhost:44321/swagger/index.html">https://localhost:44321/swagger/index.html</a>", Service consumers can access SwaggerUI to run tests on the service to understand the output of the service calls, without using any third party tools. </p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-sEGkA_PdgGM/XzsjytwTcyI/AAAAAAAAHf0/GN5EcCaawjkcs0SipZjoxM7UDCSv0OqpgCLcBGAsYHQ/s733/6.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="733" data-original-width="676" height="640" src="https://1.bp.blogspot.com/-sEGkA_PdgGM/XzsjytwTcyI/AAAAAAAAHf0/GN5EcCaawjkcs0SipZjoxM7UDCSv0OqpgCLcBGAsYHQ/s640/6.png" /></a></div><p>let me know if it helps. Thank you.</p>Pratap Reddy Pilakahttp://www.blogger.com/profile/17141976956878163883noreply@blogger.com1tag:blogger.com,1999:blog-4208378482021219922.post-16676980153541124732020-08-15T08:12:00.017-07:002020-08-23T16:00:12.932-07:00Azure DevOps 101 - Creating your first Build and Release Pipelines for automated deployments to DEV, QA and PROD <p><b>DevOps: </b>I cannot do justice to DevOps in a single article. Its an Ocean. But for our purposes here is what i would say if some one asks me about it.</p><p><span style="font-size: x-large;">"DevOps is a practice in which all the stakeholders were actively involved in entire lifecycle of product from Development , Testing, Productionizing and Production Support."</span></p><p>Core values of DevOps defined by CAMS - Culture, Automation, Measurement and Sharing.</p><p>This is eagle eye view of the core values.</p><p>Lets talk about second core value, Automation. Its not about just automation of everything in software life cycle.</p><p><b>Automation:</b> It talks about fastening stages of SDLC with continuous integration and delivery thus reducing effort and time to roll new changes. Multiple changes getting tested and deployed in a day - make people's life a lot better. Please keep in mind "Automation" in DevOps means giving prefference to "People over Process over Tools". </p><p>Please reffer to various guides or gurus for deep dive into DevOps.</p><p>Our objective here is to implement CI/CD (Continuous Integration / Continues Delivery) in Azure and learn how to cofigure your first Azure Devops Build and Release pipelines. Using which we will push our code to various stages DEV, QA and PROD. </p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-qOfi5OajaVI/XzfEi6W_SWI/AAAAAAAAHXM/QAv3LWCKeZMDHty5HILTB8Xgp_4AsRJvgCLcBGAsYHQ/s640/microsoft-azure-devops-pipeline-xenonstack.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="360" data-original-width="640" src="https://1.bp.blogspot.com/-qOfi5OajaVI/XzfEi6W_SWI/AAAAAAAAHXM/QAv3LWCKeZMDHty5HILTB8Xgp_4AsRJvgCLcBGAsYHQ/s0/microsoft-azure-devops-pipeline-xenonstack.png" /></a></div><a name='more'></a><p><b>Pre-Requisites:</b> You should have Azure subscription ; Visual Studio and Git installed on your machine.</p><p><span style="font-size: x-large;"><b>Step 1:</b> </span>First i am logging in to Azure console and creating 3 WebApps with DEV, QA and PROD attached in thier names. I am using .Net Core 2.1 code , so i am using same framework for the Azure WebApps.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-1UBwy0PogxU/XzfJKiwSMhI/AAAAAAAAHXY/zgszpPEbzdQ6XHF6Xo6bfROzKAOJpl9qgCLcBGAsYHQ/s782/1.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="533" data-original-width="782" src="https://1.bp.blogspot.com/-1UBwy0PogxU/XzfJKiwSMhI/AAAAAAAAHXY/zgszpPEbzdQ6XHF6Xo6bfROzKAOJpl9qgCLcBGAsYHQ/s640/1.png" width="640" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-mSDX_Olyu9o/XzfJMbkS7TI/AAAAAAAAHXc/gGt1Wbp61rE6Ic1j1qblR2uxP_mYd93qwCLcBGAsYHQ/s848/2.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="364" data-original-width="848" src="https://1.bp.blogspot.com/-mSDX_Olyu9o/XzfJMbkS7TI/AAAAAAAAHXc/gGt1Wbp61rE6Ic1j1qblR2uxP_mYd93qwCLcBGAsYHQ/s640/2.png" width="640" /></a></div><p>Great. There is nothing deployed to these app services yet.</p><p><b><span style="font-size: x-large;">Step 2:</span> </b>Open Visual studio and create a .Net Core web application targetting Core 2.1 Framework.</p><p> I am using VS2019 here. Please make cure you installed Git on dev machine before you create this project. </p><p>Choose "ASP.Net Core Webapplication" in templates and then Choose "Web Application" and Framework 2.1 as shown below.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-F8QwfFD6_9E/XzfK7ldiksI/AAAAAAAAHXs/hV2nN2ea2n8CoWZYa5hVUKjj6M7Glg_BACLcBGAsYHQ/s981/3.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="645" data-original-width="981" src="https://1.bp.blogspot.com/-F8QwfFD6_9E/XzfK7ldiksI/AAAAAAAAHXs/hV2nN2ea2n8CoWZYa5hVUKjj6M7Glg_BACLcBGAsYHQ/s640/3.png" width="640" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-5_iaLy8dBwA/XzfK7iBqoMI/AAAAAAAAHXw/O__y43ypQNorbTFjKVl8mcRaobkEPyndACLcBGAsYHQ/s988/4.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="622" data-original-width="988" src="https://1.bp.blogspot.com/-5_iaLy8dBwA/XzfK7iBqoMI/AAAAAAAAHXw/O__y43ypQNorbTFjKVl8mcRaobkEPyndACLcBGAsYHQ/s640/4.png" width="640" /></a></div><p>Great. Project is sucessfully created. Press F5 to see the output. Look at the Url. </p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-WkAPvnb3A-E/XzfMDrBr0NI/AAAAAAAAHYA/QrLCQLBzv3Q23o6vS4Qz4f8cwbvIwHvLgCLcBGAsYHQ/s1378/5.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="622" data-original-width="1378" src="https://1.bp.blogspot.com/-WkAPvnb3A-E/XzfMDrBr0NI/AAAAAAAAHYA/QrLCQLBzv3Q23o6vS4Qz4f8cwbvIwHvLgCLcBGAsYHQ/s640/5.png" width="640" /></a></div><p><span style="font-size: x-large;"><b>Step 3:</b></span> Lets deploy this to Azure WebApp in DEV.</p><p>Right click on solution and click publish. If you are using the same account for both visual studio and Azure subscription, Visual studio will automatically display your subscription and available webapps to deploy. If not you may need to configure a dpeloyment profile in which you need to give subscription details.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-ZUSUZsuF7Ks/XzfNhJ8-79I/AAAAAAAAHYM/VLlg2L0cSIYcIiZD-8pAP8G9GLNpE64oACLcBGAsYHQ/s813/6.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="813" data-original-width="809" height="640" src="https://1.bp.blogspot.com/-ZUSUZsuF7Ks/XzfNhJ8-79I/AAAAAAAAHYM/VLlg2L0cSIYcIiZD-8pAP8G9GLNpE64oACLcBGAsYHQ/s640/6.png" /></a></div><p>Now once the deployment is finished , browse your dev version of App Service with name "AZWebApp-Dev" and here is the out put. Look at the url.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-HsEGlQ-sLBs/XzfN8TuJxSI/AAAAAAAAHYU/kiWcOTsOOOEkUfOHDR27BxVC9MoOrCLjQCLcBGAsYHQ/s1441/7.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="577" data-original-width="1441" src="https://1.bp.blogspot.com/-HsEGlQ-sLBs/XzfN8TuJxSI/AAAAAAAAHYU/kiWcOTsOOOEkUfOHDR27BxVC9MoOrCLjQCLcBGAsYHQ/s640/7.png" width="640" /></a></div><p>Wait we talked about Automation, CI/CD , Azure DevOps. But what we did is a manual deployment. </p><p>This is not ideal. So lets automate this.</p><p><span style="font-size: x-large;"><b>Step 4:</b></span> Go to Visual Studio where the project is opened currently. Open terminal from "View" menu.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-1n3pHS1fBMU/XzfPurF5hnI/AAAAAAAAHYg/ZGwKETENlHErN9rlx26lDHUfTvb1EgUSwCLcBGAsYHQ/s624/8.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="149" data-original-width="624" src="https://1.bp.blogspot.com/-1n3pHS1fBMU/XzfPurF5hnI/AAAAAAAAHYg/ZGwKETENlHErN9rlx26lDHUfTvb1EgUSwCLcBGAsYHQ/s0/8.png" /></a></div><p>Now you see that all of your project files are committed to your local git repository.</p><p>To view your local git repo, go to "Team Explorer", click on the bottom Git symbol, you will see local repos. But initially your new repo wont be shown, so add it as shown below and give the project folder path and it will show up.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-b5FXr-NN7x8/XzfRG1tIqdI/AAAAAAAAHYs/0V5pQBYKryUmnfRdTn5OHItgllMZebmnACLcBGAsYHQ/s467/9.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="467" data-original-width="359" src="https://1.bp.blogspot.com/-b5FXr-NN7x8/XzfRG1tIqdI/AAAAAAAAHYs/0V5pQBYKryUmnfRdTn5OHItgllMZebmnACLcBGAsYHQ/s0/9.png" /></a></div><p>Now once it is added, click on "AZWebApp" repo, you see both your default repo and the new one. Double click on new "AZWebApp" repo and close the VisualStudio and reopen and open project. This is what you will see. </p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-_8IFXyay3DA/XzfR6r_yorI/AAAAAAAAHY0/Xo8WtFaCILgxC5l9UpHocQwbrDinRK52QCLcBGAsYHQ/s355/10.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="89" data-original-width="355" src="https://1.bp.blogspot.com/-_8IFXyay3DA/XzfR6r_yorI/AAAAAAAAHY0/Xo8WtFaCILgxC5l9UpHocQwbrDinRK52QCLcBGAsYHQ/s0/10.png" /></a></div><p>You can remove the default repo and make sure the current repo is highlighted as shown below.</p><p><span style="font-size: x-large;"><b>Step 5:</b></span> Go to Azure console and search for "Azure DevOps Organizations", and click on it.</p><p>You may need to create an organization, which is registering a unique organization assciation with your subscription. Here is mine.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-FfDClYU2pWE/XzfTln16OjI/AAAAAAAAHZA/ub-FKS9J5CY0ef67aB1aRQcTlrB70KYVwCLcBGAsYHQ/s915/11.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="261" data-original-width="915" src="https://1.bp.blogspot.com/-FfDClYU2pWE/XzfTln16OjI/AAAAAAAAHZA/ub-FKS9J5CY0ef67aB1aRQcTlrB70KYVwCLcBGAsYHQ/s640/11.png" width="640" /></a></div><p><br /></p><p>Create an Organization and its time to create a Project under that. Click on the link "New Project".</p><p>I created my Project with name "Demo" and selected Git as the source control for this. I kept it a private project as i dont bother to share it with anyone. </p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-VpuRP1v6vUM/XzfURWaEAcI/AAAAAAAAHZI/WN6GbOj5ZMsGKkN_KS9mtJcvrUQfPt4uACLcBGAsYHQ/s622/12.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="622" data-original-width="615" src="https://1.bp.blogspot.com/-VpuRP1v6vUM/XzfURWaEAcI/AAAAAAAAHZI/WN6GbOj5ZMsGKkN_KS9mtJcvrUQfPt4uACLcBGAsYHQ/s0/12.png" /></a></div><p>Once the project si created, you will be taken to a page where you see various Icons which were shown below.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-HUUlF3WU2qo/XzfU7FBTdaI/AAAAAAAAHZQ/qWx_ZS-eHp8DHLJ3Tr9pPsuGQAKT_QSrACLcBGAsYHQ/s1029/13.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="469" data-original-width="1029" src="https://1.bp.blogspot.com/-HUUlF3WU2qo/XzfU7FBTdaI/AAAAAAAAHZQ/qWx_ZS-eHp8DHLJ3Tr9pPsuGQAKT_QSrACLcBGAsYHQ/s640/13.png" width="640" /></a></div><p>On your project summary page , in the left side pannel, click on Repos. Now it will say that "Demo os empty add some code to it."</p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-AlZJyvNdmmA/XzfVXydAgPI/AAAAAAAAHZY/m09hPUiwVZk1hYBymtaQjbnV3lPM9QcigCLcBGAsYHQ/s924/14.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="299" data-original-width="924" src="https://1.bp.blogspot.com/-AlZJyvNdmmA/XzfVXydAgPI/AAAAAAAAHZY/m09hPUiwVZk1hYBymtaQjbnV3lPM9QcigCLcBGAsYHQ/s640/14.png" width="640" /></a></div><p>Click on the "+" button and add a new repository. I names it "AZWebAppRepo" and choose Git Ignore as Visul Studio, as i want unnecessary files form VS to be checked in.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-fs_LAolZ_Vk/XzfWB29YEpI/AAAAAAAAHZk/tDCOlZXcJnsNT5vxJCpWd_n5Muqp93BrwCLcBGAsYHQ/s481/15.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="354" data-original-width="481" src="https://1.bp.blogspot.com/-fs_LAolZ_Vk/XzfWB29YEpI/AAAAAAAAHZk/tDCOlZXcJnsNT5vxJCpWd_n5Muqp93BrwCLcBGAsYHQ/s0/15.png" /></a></div><p>Once created, you can see there will be only 2 files. ReamMe text file and a gitignore file.</p><p>Its time to push our code to this repo.</p><p><span style="font-size: x-large;"><b>Step 6:</b></span> Go to Visual studio and open the project and go to "Team Explorer". Click on "Manage Connections" and select "Connect to a Project" option. A window will be opened with your azure DevOps Organizations listed. Click on Organization (in my case "Demo") and click Connect.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-r-dHoA5Ta1k/XzfXB-XlAxI/AAAAAAAAHZw/-5AcRdP4SEswH68VSMtJF9uFNlLD3GTpgCLcBGAsYHQ/s803/16.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="357" data-original-width="803" src="https://1.bp.blogspot.com/-r-dHoA5Ta1k/XzfXB-XlAxI/AAAAAAAAHZw/-5AcRdP4SEswH68VSMtJF9uFNlLD3GTpgCLcBGAsYHQ/s640/16.png" width="640" /></a></div><p>Now you will see like shown below in your "Team Explorer". Select your Azure Devops organization name and click on the drop arrow highlighted. Now click on "Sync" option. By this we are asking to synch the local Git repo to the Repo on our Azure Devops organization.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-qI4L4ML_T38/XzfX4I-FwtI/AAAAAAAAHZ8/G3MlHRALaCgvkCmr8erB5AmPDkVpcLLagCLcBGAsYHQ/s318/17.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="318" data-original-width="224" src="https://1.bp.blogspot.com/-qI4L4ML_T38/XzfX4I-FwtI/AAAAAAAAHZ8/G3MlHRALaCgvkCmr8erB5AmPDkVpcLLagCLcBGAsYHQ/s0/17.png" /></a></div><p>It should comeup with a window like this. Dont worry if it doesnt. Some times you may need to re-open the project one more time from Visual studio with out closing the IDE after connecting to Azure Organization.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-B_wwK-Xc7Fc/XzfZfldPFYI/AAAAAAAAHaI/ZjcdSMCak7crqmBYlwgX2Sk6EH3nnVoggCLcBGAsYHQ/s469/18.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="469" data-original-width="393" src="https://1.bp.blogspot.com/-B_wwK-Xc7Fc/XzfZfldPFYI/AAAAAAAAHaI/ZjcdSMCak7crqmBYlwgX2Sk6EH3nnVoggCLcBGAsYHQ/s0/18.png" /></a></div><p>Publish the current local repo to Azure Devops Repo by clicking the highlighted button. now you have to select the Organization, the Project and the Repo we created in Step 5. Here is mine.</p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-2U0_405CfdM/Xzfc1rkG__I/AAAAAAAAHac/rJ6XFVMUtNg-NjFODrPZmQZubVsjadozQCLcBGAsYHQ/s430/19.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="430" data-original-width="397" src="https://1.bp.blogspot.com/-2U0_405CfdM/Xzfc1rkG__I/AAAAAAAAHac/rJ6XFVMUtNg-NjFODrPZmQZubVsjadozQCLcBGAsYHQ/s0/19.png" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div> Publish repository and you get a sucess message. <div>You might get an error message saying</div><div> </div><div><b>"Error encountered while pushing branch to the remote repository: rejected Updates were rejected because the remote contains work that you do not have locally. This is usually caused by another repository pushing to the same ref. You may want to first integrate the remote changes before pushing again."</b></div><div><b><br /></b></div><div>This is because of the conflicts you trying mutiple times to publish and some conflicts between local and Azure repos. Use below command and it will be done.</div><div><br /></div><div><b><i><span style="font-size: x-large;">git push -f origin master</span></i></b></div><div><p></p><p>Now go back to Azure Repo we created in Step 5. You can find all the code published to Azure Repo.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-c-jf3xRGWlE/XzfdwTpAdGI/AAAAAAAAHao/9eXovggD2Fk84FBDrZwzfqacNy2oN14_QCLcBGAsYHQ/s1393/20.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="487" data-original-width="1393" src="https://1.bp.blogspot.com/-c-jf3xRGWlE/XzfdwTpAdGI/AAAAAAAAHao/9eXovggD2Fk84FBDrZwzfqacNy2oN14_QCLcBGAsYHQ/s640/20.png" width="640" /></a></div><p><span style="font-size: x-large;"><b>Step 7:</b></span> We have created Repo, now we need to go for Build Pipeline.</p><p>Go to Azure portal and open your Azure Project. You see a rocket shaped icon which says "Pipeline". Click on it. We havent configured any pipelines yet so lets start by clicking Create new Pipeline.</p><p>Now if you are a pro in Yaml configuration, please go ahead and create your pipeline. But for first time please click the link which says <span style="font-size: x-large; font-weight: bold;">"</span><a class="bolt-link" href="https://dev.azure.com/pratappilaka/Demo/_apps/hub/ms.vss-ciworkflow.build-ci-hub?_a=build-definition-getting-started&id=0" style="background-color: white; border-radius: 2px; box-sizing: inherit; cursor: pointer; font-size: x-large; font-weight: bold; outline: transparent; text-decoration-line: none; transition: all 80ms cubic-bezier(0.165, 0.84, 0.44, 1) 0s, all 80ms linear 0s;" tabindex="0">Use the classic editor</a><span style="font-size: x-large; font-weight: bold;">" </span><span>Which will take us to this.</span></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-QAo6bvs_VkE/XzfiRn2NIaI/AAAAAAAAHa0/GJebCADQ27MHZPrY5gLoYgnbC3P2MnuUACLcBGAsYHQ/s483/21.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="483" data-original-width="459" src="https://1.bp.blogspot.com/-QAo6bvs_VkE/XzfiRn2NIaI/AAAAAAAAHa0/GJebCADQ27MHZPrY5gLoYgnbC3P2MnuUACLcBGAsYHQ/s0/21.png" /></a></div><span>Next step is to select which template we want to use. Select ASP.Net Core (.Net Framework) and lcick apply.</span><p></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-X-iz_MnRXb4/Xzfirg9wLJI/AAAAAAAAHa8/WvnSu0ZpFeoCpIzrZxvQjWm_TZiC7vjbACLcBGAsYHQ/s780/22.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="435" data-original-width="780" src="https://1.bp.blogspot.com/-X-iz_MnRXb4/Xzfirg9wLJI/AAAAAAAAHa8/WvnSu0ZpFeoCpIzrZxvQjWm_TZiC7vjbACLcBGAsYHQ/s640/22.png" width="640" /></a></div><span>Now you will see a GUI to configure various tasks. First rename the Pipeline, and the Agent which i have highlighted below. You many need to add various tasks based on additional dependecies and Nuget packages you have installed. But in our case ours is vanilla code. So we rename the Build pipeline and gaent and save.</span><p></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-poJXx5HMTgY/XzfkLVIv__I/AAAAAAAAHbI/HMFImhhSSg4crISe2-L5sj4qwMIfCaRAQCLcBGAsYHQ/s654/23.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="389" data-original-width="654" src="https://1.bp.blogspot.com/-poJXx5HMTgY/XzfkLVIv__I/AAAAAAAAHbI/HMFImhhSSg4crISe2-L5sj4qwMIfCaRAQCLcBGAsYHQ/s640/23.png" width="640" /></a></div><span><br /></span><p></p><p>Now click on trigger section. and select "Enable Continious Integration" checkbox. By doing this we are saying rollout a build everytime i check in the code to Azure Repo.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-C9DKnacwNA4/XzflPslVO-I/AAAAAAAAHbU/65mCwJ81AjM2tF3y7b4zTPk4fod3hDvVgCLcBGAsYHQ/s936/24.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="477" data-original-width="936" src="https://1.bp.blogspot.com/-C9DKnacwNA4/XzflPslVO-I/AAAAAAAAHbU/65mCwJ81AjM2tF3y7b4zTPk4fod3hDvVgCLcBGAsYHQ/s640/24.png" width="640" /></a></div><p>Click Save and give appriproate comments. With this our build pipeline has been configured.</p><p><span style="font-size: x-large;"><b>Step 8:</b></span> Lets create a "Release Pipeline". On Azure portal, go to Project page and in the same Pipeline menu on left side pannel, click on "Releases" this time. Click on "Create New Release Pipeline" button.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-JEfqwMAoXpI/Xzfma29_6lI/AAAAAAAAHbg/Hvn3zIVuiP43ltVSewAtVkyh2wRVttjLACLcBGAsYHQ/s1309/25.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="409" data-original-width="1309" src="https://1.bp.blogspot.com/-JEfqwMAoXpI/Xzfma29_6lI/AAAAAAAAHbg/Hvn3zIVuiP43ltVSewAtVkyh2wRVttjLACLcBGAsYHQ/s640/25.png" width="640" /></a></div><p><br /></p><p>once you applied "Azure App Service Deployment" template, rename the the stage to "Development".</p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-joBMXiAc8N4/Xzfm7kzuTjI/AAAAAAAAHbo/tylJNEJXo8QRqblIXGE_wtQSWEEU2eQCACLcBGAsYHQ/s278/26.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="122" data-original-width="278" src="https://1.bp.blogspot.com/-joBMXiAc8N4/Xzfm7kzuTjI/AAAAAAAAHbo/tylJNEJXo8QRqblIXGE_wtQSWEEU2eQCACLcBGAsYHQ/s0/26.png" /></a></div><p>Now click on "1 job, 1 task" link and it will take you to Tasks pane.</p><p>Here you need to select the Azure subscription, and Authenticate. Once authentication is sucessful, you will see available App Sevrices to which you can deploy. I select "AZWebApp-Dev" as this is Development stage.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-OX3OhRrTxXk/XzfoWW2YgPI/AAAAAAAAHb0/rXQEiWOcS6Iwt5gQKTB5gB35-sv09ToPgCLcBGAsYHQ/s981/27.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="500" data-original-width="981" src="https://1.bp.blogspot.com/-OX3OhRrTxXk/XzfoWW2YgPI/AAAAAAAAHb0/rXQEiWOcS6Iwt5gQKTB5gB35-sv09ToPgCLcBGAsYHQ/s640/27.png" width="640" /></a></div><p>Save it and come back to PipeLine tab. We have configured to which WebApp it has to deploy. Now we have to configure what has to be deployed and from where. Click on "Artifact" icon. This will give some options and we have to choose the Build pipe line option and select the Build pipeline we created in Step 7.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-OTz5kfnF9O4/Xzfo9YAb6aI/AAAAAAAAHb8/dYVGGeOYqKE9dF2SgvjGPYBFRl8zkD9fACLcBGAsYHQ/s1330/28.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="659" data-original-width="1330" src="https://1.bp.blogspot.com/-OTz5kfnF9O4/Xzfo9YAb6aI/AAAAAAAAHb8/dYVGGeOYqKE9dF2SgvjGPYBFRl8zkD9fACLcBGAsYHQ/s640/28.png" width="640" /></a></div><p>Add the Build pipeline to artifact and save the Release pipeline. Now pay close attention to little lightning icon on our artifact. Its a trigger. Click on it. </p><p>On your right hand side you see a toggle for enabling continious delivery. this means everytime we have a new build form our Build pipeline, we want a new release to be rolled out. </p><p>Edit the release pipeline name and save it.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-559atIUJx3g/XzfqT-V-J7I/AAAAAAAAHcI/A1NqouXBlhEzjzS5tiPj_FtQBBAaWd9HgCLcBGAsYHQ/s817/29.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="363" data-original-width="817" src="https://1.bp.blogspot.com/-559atIUJx3g/XzfqT-V-J7I/AAAAAAAAHcI/A1NqouXBlhEzjzS5tiPj_FtQBBAaWd9HgCLcBGAsYHQ/s640/29.png" width="640" /></a></div><p>With this we have configured both our Build and Release pipelines for a basic vanila ASP.Net Core webapplication.</p><p><span style="font-size: x-large;"><b>Step 9:</b></span> Lets modify the code in our ASP.Net Core application. Open Visual Studio and open the project.</p><p>I want to make a small chage to code by editing the ./Pages/Shared/_Layouts.cshtml and changing the "AZWebApp" name to "AZWeApp_Update".</p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-AX_wNqZkEHY/Xzfr6uQn3lI/AAAAAAAAHcU/OfETRBcO6OQa0iGYB91nNLa49oOA85PxACLcBGAsYHQ/s641/30.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="233" data-original-width="641" src="https://1.bp.blogspot.com/-AX_wNqZkEHY/Xzfr6uQn3lI/AAAAAAAAHcU/OfETRBcO6OQa0iGYB91nNLa49oOA85PxACLcBGAsYHQ/s640/30.png" width="640" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-Iod-yOP2CEY/XzfsesIfpKI/AAAAAAAAHcc/jWSa5M82niQGyeZkcWqKj7dsDu0ldID4QCLcBGAsYHQ/s878/31.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="184" data-original-width="878" src="https://1.bp.blogspot.com/-Iod-yOP2CEY/XzfsesIfpKI/AAAAAAAAHcc/jWSa5M82niQGyeZkcWqKj7dsDu0ldID4QCLcBGAsYHQ/s640/31.png" width="640" /></a></div><p>Save it and now go to "Team Explorer". Bottom you see a small Pencil Icon showing number of files changed. Click on it. Now you see "_Layouts.cshtml" file as one of them. Right click on the file and sleect Stage. This means we are trying to make this file as part of next commit. Now "_Layouts.cshtml" file will be shown under staged files.Give the proper Comment as this will be displayed toughout your automated deployment. </p><p>Please understand that we have commited the change to Local Git Repo, not the Azure Repo.</p><p>Inorder to do that, in Team Explorer, there is a small "Plug" icon with in connection manager. Click on it and no select Azure Repo and click on the down arrow shown below and select "Sync".</p><p>Now you san see the Push Option enabled as there is a Local Git Repo Commit available that is not commited to Azure repo. Click Push. With this we checked in the code change to Azure Repo.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-ZPAAp4nlgDs/XzfvHzRmY0I/AAAAAAAAHco/yK0tfLNH3J80t6cJslROL-nq_TdNSHiQQCLcBGAsYHQ/s850/33.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="350" data-original-width="850" src="https://1.bp.blogspot.com/-ZPAAp4nlgDs/XzfvHzRmY0I/AAAAAAAAHco/yK0tfLNH3J80t6cJslROL-nq_TdNSHiQQCLcBGAsYHQ/s640/33.png" width="640" /></a></div><p>Now go to Azure portal and open Azure Devops Project we created "Demo". Click on "Pipeline" on left side pannel. You can see an automated build was rolledout due to ou code check-in.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-p_NE_7Veqz8/Xzfw2eVRZtI/AAAAAAAAHc0/x-khaS1AxC4e2y93xnI1dEmk5NP1yefMgCLcBGAsYHQ/s1145/35.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="419" data-original-width="1145" src="https://1.bp.blogspot.com/-p_NE_7Veqz8/Xzfw2eVRZtI/AAAAAAAAHc0/x-khaS1AxC4e2y93xnI1dEmk5NP1yefMgCLcBGAsYHQ/s640/35.png" width="640" /></a></div><p>Now go to Releases and you will see a automated build was released to Development stage, which is still in-progress here.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-NFFZME-1NDA/XzfxIK_q9oI/AAAAAAAAHc8/CKTJgqjM5iEE5ocy6WRVvAuJ9_Qaj3HtQCLcBGAsYHQ/s1349/34.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="420" data-original-width="1349" src="https://1.bp.blogspot.com/-NFFZME-1NDA/XzfxIK_q9oI/AAAAAAAAHc8/CKTJgqjM5iEE5ocy6WRVvAuJ9_Qaj3HtQCLcBGAsYHQ/s640/34.png" width="640" /></a></div><p>Wait until it is completed and now click on the Release task and you will see this.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-8LKYoafHb1o/XzfxXzEm_dI/AAAAAAAAHdE/N5HvRA3YyjkX03MaU27g5QhJ8GDrv_kbwCLcBGAsYHQ/s705/36.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="495" data-original-width="705" src="https://1.bp.blogspot.com/-8LKYoafHb1o/XzfxXzEm_dI/AAAAAAAAHdE/N5HvRA3YyjkX03MaU27g5QhJ8GDrv_kbwCLcBGAsYHQ/s640/36.png" width="640" /></a></div><p>Lets browse our Azure App Sevice "AZWebApp-Dev". Here is the new code running in Azure WebApp dployed using Azure DevOps CI/CD.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-aKLiikhndCo/Xzfx2oeV-EI/AAAAAAAAHdQ/c4p2IHXDM9UXSObPkVWMiJ6e6WeIgUvIwCLcBGAsYHQ/s573/37.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="344" data-original-width="573" src="https://1.bp.blogspot.com/-aKLiikhndCo/Xzfx2oeV-EI/AAAAAAAAHdQ/c4p2IHXDM9UXSObPkVWMiJ6e6WeIgUvIwCLcBGAsYHQ/s0/37.png" /></a></div><p>Its not yet over. But we are close. Now we need to configure two other stages, QA and PROD.</p><p>Step 10: Open Azure Devops Project and navigate to Release pipeline we created earlier.</p><p>Repeat everything we done in Step 8 and select App Service "AZWebApp-QA" for QA stage and "AZWebApp-Prod" for Production stage.</p><p>Unlike DEV, We dont want to roll out every change to QA and PROD. So pay attention to little icon in front of QA and PROD stages.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-VCy-QCop4tQ/Xzf1ggVZPUI/AAAAAAAAHdc/3NOP-dIErYElP81o3wft__wh4MuGwDp1wCLcBGAsYHQ/s1254/39.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="431" data-original-width="1254" src="https://1.bp.blogspot.com/-VCy-QCop4tQ/Xzf1ggVZPUI/AAAAAAAAHdc/3NOP-dIErYElP81o3wft__wh4MuGwDp1wCLcBGAsYHQ/s640/39.png" width="640" /></a></div><p>When you click that Icon representing "Pre-Deployment Conditions", there is an option to enable Approvals over what to be deployed. Add the right users to the approval group and do the same for PROD as well.</p><p>Now we are all set for an End to End deployment using basic Azure DevOps CI/CD.</p><p>Repeat Step 9, changing the code. Now i am changing "AZWebApp_Update" to "AZWebApp_Finale". Staged the chnage and checking in the code. This should trigger automated deployment.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-nUsVv_kSf3M/Xzf3T3HRzoI/AAAAAAAAHdo/8BBClO5M8cMw3FVw7FsADAq453N2d1VZwCLcBGAsYHQ/s419/40.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="295" data-original-width="419" src="https://1.bp.blogspot.com/-nUsVv_kSf3M/Xzf3T3HRzoI/AAAAAAAAHdo/8BBClO5M8cMw3FVw7FsADAq453N2d1VZwCLcBGAsYHQ/s0/40.png" /></a></div><p>You can see an ongoing build in Build Pipeline.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-sElMVAVfRFc/Xzf3wQMM1CI/AAAAAAAAHdw/o9AdH9Gxr4IVS5okaqqtrDXlfH9xP6-_QCLcBGAsYHQ/s1174/41.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="259" data-original-width="1174" src="https://1.bp.blogspot.com/-sElMVAVfRFc/Xzf3wQMM1CI/AAAAAAAAHdw/o9AdH9Gxr4IVS5okaqqtrDXlfH9xP6-_QCLcBGAsYHQ/s640/41.png" width="640" /></a></div><p>Once Build is sucesfully rolledout, lets go to Release Pipeline.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-heKp9jChRlw/Xzf4OfnZZLI/AAAAAAAAHd8/oft9irX4vmoUavd-qU4vY_ehVqWRdcESgCLcBGAsYHQ/s1002/42.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="286" data-original-width="1002" src="https://1.bp.blogspot.com/-heKp9jChRlw/Xzf4OfnZZLI/AAAAAAAAHd8/oft9irX4vmoUavd-qU4vY_ehVqWRdcESgCLcBGAsYHQ/s640/42.png" width="640" /></a></div><p>Once Dev is Completed, you will see QA is pending with our Approval.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-5lUx2ZMArb4/Xzf4i9QdNqI/AAAAAAAAHeE/E0VfEAosKWwowwsvi0jYAt4nZ9T1ewB-QCLcBGAsYHQ/s936/43.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="336" data-original-width="936" src="https://1.bp.blogspot.com/-5lUx2ZMArb4/Xzf4i9QdNqI/AAAAAAAAHeE/E0VfEAosKWwowwsvi0jYAt4nZ9T1ewB-QCLcBGAsYHQ/s640/43.png" width="640" /></a></div><p>QA Deployment is finished and now PROD release is waiting for my Approval. After Approval, the PROD release starts.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-vS2jx67nyb0/Xzf5wHVrpeI/AAAAAAAAHeQ/EeMfs3DPiVUcSlGbm_RPPCBMz14X6qRzgCLcBGAsYHQ/s981/45.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="281" data-original-width="981" src="https://1.bp.blogspot.com/-vS2jx67nyb0/Xzf5wHVrpeI/AAAAAAAAHeQ/EeMfs3DPiVUcSlGbm_RPPCBMz14X6qRzgCLcBGAsYHQ/s640/45.png" width="640" /></a></div><p>Finally PROD deployment is also finished.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-FWqHwcIRzFY/Xzf6EftnfxI/AAAAAAAAHeY/BfMAbbaoCu4YCUEpd5kThgvaXD-F4wJ_gCLcBGAsYHQ/s986/46.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="270" data-original-width="986" src="https://1.bp.blogspot.com/-FWqHwcIRzFY/Xzf6EftnfxI/AAAAAAAAHeY/BfMAbbaoCu4YCUEpd5kThgvaXD-F4wJ_gCLcBGAsYHQ/s640/46.png" width="640" /></a></div><p>Lets check the final output on PROD App Service.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-tioJxkwgzek/Xzf6vPHO5iI/AAAAAAAAHek/6Jczlo8KOssW_0d00ZY5xpdWTqLZKU42QCLcBGAsYHQ/s1202/47.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="370" data-original-width="1202" src="https://1.bp.blogspot.com/-tioJxkwgzek/Xzf6vPHO5iI/AAAAAAAAHek/6Jczlo8KOssW_0d00ZY5xpdWTqLZKU42QCLcBGAsYHQ/s640/47.png" width="640" /></a></div><p>Ok, now we are done. Happy Learning.</p></div>Pratap Reddy Pilakahttp://www.blogger.com/profile/17141976956878163883noreply@blogger.com0tag:blogger.com,1999:blog-4208378482021219922.post-32066028780094817022020-07-27T02:13:00.001-07:002020-07-27T02:13:31.757-07:00Terraform - Provisioning Infrastructure level Security in AWS A year ago I have created an article showing how to create infrastructure-level security in AWS step by step. <b>Now its time to automate the provisioning process using Octopus and terrafrom.</b><div><br /><div>We have done it manually from AWS Management console.</div><h4 style="text-align: left;"><a href="https://pratapreddypilaka.blogspot.com/2019/08/aws-vpc-basics-how-to-configure.html" target="_blank">Here is the link for the earlier article.</a> </h4><div>Here are the components we intend to create using terrafrom.</div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-YWr-SbSang0/Xx6AJgf9KpI/AAAAAAAAHVk/EdHhmCozI68LIQTqBlL5CogNGSFMO8u1wCLcBGAsYHQ/s826/1.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="388" data-original-width="826" height="294" src="https://1.bp.blogspot.com/-YWr-SbSang0/Xx6AJgf9KpI/AAAAAAAAHVk/EdHhmCozI68LIQTqBlL5CogNGSFMO8u1wCLcBGAsYHQ/w625-h294/1.png" width="625" /></a></div><div>We will be creating VPC, Public and Private Subnets, RoutTables, Security Groups, Inbound / Outbound rules for communication channels on each of the security groups.</div><div><a name='more'></a></div><div>First i would recommend creating all this components manually so that you know in and out of the significance of each component and know the dpeendecies of each of those components on others.</div><div><br /></div><div>Read the above mentioned article for manual process and indetail explanation of each of the components.</div><div><br /></div><div>Now coming to Terrafrom, i hope you have read the "<a href="https://pratapreddypilaka.blogspot.com/2020/07/iac-infrastructure-as-code-using.html" target="_blank">IaC Infrastructure as Code using Terraform - Basics</a>" and "<a href="https://pratapreddypilaka.blogspot.com/2020/07/iac-terraform-automation-using-octopus.html" target="_blank">IaC - Terraform Automation using Octopus Deploy</a>" articles from earlier. These articles will give basic understanding and implementation of Octopus integration with terrafroms from scratch.</div><div><br /></div><div>By now if you follow above articles you should be able to create Octopus projects and create deployment steps as part of process and able to deploy componenets (S3 Bucket) to AWS.</div><div><br /></div><div>We will add two additional steps to process to deal with creation and deletion of all the components shown above.</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-KhWqrPmq7uk/Xx6QrS4cp9I/AAAAAAAAHVw/fX_xLxiDl0EOzQdLje7-FZMZZmxqgp-wACLcBGAsYHQ/s969/21.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="523" data-original-width="969" height="338" src="https://1.bp.blogspot.com/-KhWqrPmq7uk/Xx6QrS4cp9I/AAAAAAAAHVw/fX_xLxiDl0EOzQdLje7-FZMZZmxqgp-wACLcBGAsYHQ/w625-h338/21.png" width="625" /></a></div><div><br /></div><div><br /></div><div>Now lets deal with code required to create the components shown in above picture.</div><div><br /></div><div><pre>terraform {
backend "s3" {
bucket = "#{terraformstates3bucket}"
key = "#{terraformvpcstatekey}"
region = "#{awsregion}"
}
}
provider "aws" {
region="#{awsregion}"
}
resource "aws_vpc" "tf_vpc" {
cidr_block = "10.0.0.0/16"
enable_dns_hostnames = true
enable_dns_support = true
tags {
Name = "My Octo AWS VPC"
}
}
resource "aws_internet_gateway" "tf_internet_gateway" {
vpc_id = "${aws_vpc.tf_vpc.id}"
tags {
Name = "My Octo AWS IGW"
}
}
resource "aws_route_table" "tf_public_rt" {
vpc_id = "${aws_vpc.tf_vpc.id}"
route {
cidr_block = "0.0.0.0/0"
gateway_id = "${aws_internet_gateway.tf_internet_gateway.id}"
}
tags {
Name = "My Octo AWS Pub RT"
}
}
resource "aws_default_route_table" "tf_private_rt" {
default_route_table_id = "${aws_vpc.tf_vpc.default_route_table_id}"
tags {
Name = "My Octo AWS Pri RT"
}
}
resource "aws_subnet" "tf_public_subnet" {
count = 1
vpc_id = "${aws_vpc.tf_vpc.id}"
cidr_block = "10.0.0.0/24"
map_public_ip_on_launch = true
availability_zone = "us-west-2a"
tags {
Name = "My Octo AWS Pub SubNet"
}
}
resource "aws_subnet" "tf_private_subnet" {
count = 1
vpc_id = "${aws_vpc.tf_vpc.id}"
cidr_block = "10.0.1.0/24"
availability_zone = "us-west-2b"
tags {
Name = "My Octo AWS Pri SubNet"
}
}
resource "aws_route_table_association" "tf_public_assoc" {
count = "${aws_subnet.tf_public_subnet.count}"
subnet_id = "${aws_subnet.tf_public_subnet.id}"
route_table_id = "${aws_route_table.tf_public_rt.id}"
}
resource "aws_route_table_association" "tf_private_assoc" {
count = "${aws_subnet.tf_private_subnet.count}"
subnet_id = "${aws_subnet.tf_private_subnet.id}"
route_table_id = "${aws_default_route_table.tf_private_rt.id}"
}
resource "aws_security_group" "tf_public_sg" {
name = "My Octo AWS Pub SG"
description = "Used for access to the public instances"
vpc_id = "${aws_vpc.tf_vpc.id}"
#SSH
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
#HTTP
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
resource "aws_security_group" "tf_private_sg" {
name = "My Octo AWS Pri SG"
description = "Used for access to the private instances"
vpc_id = "${aws_vpc.tf_vpc.id}"
#SSH
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
security_groups = ["${aws_security_group.tf_public_sg.id}"]
}
#HTTP
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
security_groups = ["${aws_security_group.tf_public_sg.id}"]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}</pre></div><div>Add above source code to both </div><div>1. Create AWS VPC step which is a Terraform-Apply step</div><div>2. Delete AWS VPC step which is a Terraform-Destroy step</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-08R8kdQcaEY/Xx6R6FY_RKI/AAAAAAAAHV8/dWjkTFQeEEY31NKo2ftH_MiQa5MTNGftQCLcBGAsYHQ/s1326/2.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="461" data-original-width="1326" height="218" src="https://1.bp.blogspot.com/-08R8kdQcaEY/Xx6R6FY_RKI/AAAAAAAAHV8/dWjkTFQeEEY31NKo2ftH_MiQa5MTNGftQCLcBGAsYHQ/w625-h218/2.png" width="625" /></a></div><div><br /></div><div> Now i deployed the build and choose to run Create AWS VPN step.</div><div>Here is the output.</div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-9gkZst-Fffk/Xx6SVCxDhzI/AAAAAAAAHWE/MfZNiBBYojUSW91OqvkRKRPtTpXDVbBGwCLcBGAsYHQ/s1003/1.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="457" data-original-width="1003" height="286" src="https://1.bp.blogspot.com/-9gkZst-Fffk/Xx6SVCxDhzI/AAAAAAAAHWE/MfZNiBBYojUSW91OqvkRKRPtTpXDVbBGwCLcBGAsYHQ/w625-h286/1.png" width="625" /></a></div><div> You can see that irrespctive of the order i mentioned resources , terraform being a good provisioning tool, it interprets what need to be created first and which component creation should follow.</div><div><br /></div><div>I have taken individual snapshots of all the components created in AWS and pasted in a single screenshot.</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-KY3v0c2ds0c/Xx6V4TkXwPI/AAAAAAAAHWQ/sq4CHELEwAQ5oCvYnWhPoNXaeUGmYkXRgCLcBGAsYHQ/s994/3.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="646" data-original-width="994" height="406" src="https://1.bp.blogspot.com/-KY3v0c2ds0c/Xx6V4TkXwPI/AAAAAAAAHWQ/sq4CHELEwAQ5oCvYnWhPoNXaeUGmYkXRgCLcBGAsYHQ/w625-h406/3.png" width="625" /></a></div><div><br /></div><div> </div><div>When you run the "Delete AWS VPN" step, terraform will identify the dependencies between components and delete them in right order.</div><div><br /></div><div>Thus we can provision infrastructure level security boundaries in AWS using Octopus and Terrafrom.</div></div>Pratap Reddy Pilakahttp://www.blogger.com/profile/17141976956878163883noreply@blogger.com1tag:blogger.com,1999:blog-4208378482021219922.post-9876598259104647702020-07-20T00:08:00.002-07:002020-07-21T15:11:40.630-07:00IaC - Terraform Automation using Octopus Deploy<div class="separator" style="clear: both; text-align: center;"><br /></div>Octopus is a enterprise mainstream release management tool, but now its avilable for developers via cloud under tha name of "Octopus Cloud". <div>We don't need a "build server" as its is hosted by Octopus cloud and we utilize its resources for our deployments.</div><div><br /></div><div>In this article we will learn how to deploy our "Infrastructure as Code" using Terraform and Octopus deploy. Beofre that, if you need to familiarize with terrafroms basics, go to <a href="https://pratapreddypilaka.blogspot.com/2020/07/iac-infrastructure-as-code-using.html" target="_blank">earlier article</a>.</div><div><br /></div><div>I need to highlight couple of features we look, for considering this combination of Terraform and Octopus deploy.</div><div><br /></div><div><b>#1. Configuration Management Vs Provisioning: </b></div><div>We are not looking for Configuration management, as Chef and Puppet does better in that department. We want Provisioning tool which creates Infrastructure in the same state no matter what order the resources were mentioned and no matter how many times the tools was deployed.</div><div><br /></div><div><b>#2. Idempotent:</b></div><div>This feature is the ability to create the infrasturcture in exact same state even after multiple runs. Terraform doesnt suffers from "Configuration Drift" unlike Cheff and Puppet.</div><div><br /></div><div><b>#3. Economic: </b></div><div>We are dealing with what a single developer can do on his dev machine. So unlike Cheff and Puuppet we dont need a "MasterServer" to save the state or to run the centralized updates. We can either use a shared location or in our case an S3 bucket for saving the state.</div><div><a name='more'></a></div><div><b><br /></b></div><div><b>Step 1:</b> Sign up for Octopus Cloud and check the features that were free for this free version. <a href="https://octopus.com/start/cloud?utm_campaign=cta-free&utm_content=sign-up" target="_blank">Click the link here</a>. </div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-jKmzRfPMx10/XxUcSUvbMmI/AAAAAAAAHQ4/HADbUERz0eMITvtGm5q0X9iOI8hzTD7UACLcBGAsYHQ/s936/1.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="528" data-original-width="936" height="354" src="https://1.bp.blogspot.com/-jKmzRfPMx10/XxUcSUvbMmI/AAAAAAAAHQ4/HADbUERz0eMITvtGm5q0X9iOI8hzTD7UACLcBGAsYHQ/w625-h354/1.png" width="625" /></a></div><div><br /></div><div><b>Step 2:</b> I am planning to target AWS for this automated deployment, as we have been working with AWS in all my recent posts. But this procedure can also be applicable if you would liek to target Azure.</div><div><br /></div><div>Signup for the AWS free tier subscription. Once you install AWS CLI, Open AWS console in browser and go to IAM.</div><div><br />Create a user "terraform" with "Adminstrator" access. Please note that this is not a ideal Prod scenario and you should never assign "Adminstrator" permissions to any IAM Users. We just want to keep things simple for our Demo. Download the <br /></div><div><br /></div><div><b>Step 3:</b> Log into your Octopus cloud portal and first thing we need to setup is an Enviornment. As we are intend to use your developer machine and deploy to AWS, we name it "AWS_Development".</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-b6z58-xNlrc/XxUg5up2-uI/AAAAAAAAHRQ/usXXwsSohMMHcbJhCuN8yMkgmuwknvyzQCLcBGAsYHQ/s1307/3.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="368" data-original-width="1307" height="176" src="https://1.bp.blogspot.com/-b6z58-xNlrc/XxUg5up2-uI/AAAAAAAAHRQ/usXXwsSohMMHcbJhCuN8yMkgmuwknvyzQCLcBGAsYHQ/w625-h176/3.png" width="625" /></a></div><div><br /></div><div> Please note that we dont need any deployment targets here as we intent to provision and not conifg management.</div><div><br /></div><div>So next step is to add a AWS Account that we will use for our deployments. Use the Access Key ID and Secret Key for adding the "terraform" account as shown below.</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-HkKDBOsty_4/XxUipELrUrI/AAAAAAAAHRc/zsj4Sc4oD5MwwRNq8xohvEL6fMMnzEukwCLcBGAsYHQ/s1316/4.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="462" data-original-width="1316" height="219" src="https://1.bp.blogspot.com/-HkKDBOsty_4/XxUipELrUrI/AAAAAAAAHRc/zsj4Sc4oD5MwwRNq8xohvEL6fMMnzEukwCLcBGAsYHQ/w625-h219/4.png" width="625" /></a></div><div><br /></div><div><b>Step 4:</b> Now create a project group and a new project. I named them as shown below.</div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-b2_l0-EJukI/XxUkcsQ8Q6I/AAAAAAAAHRo/8QYsTD4qWQ0mh53sldAbXJOEa3XhvpvPQCLcBGAsYHQ/s603/5.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="333" data-original-width="603" height="221" src="https://1.bp.blogspot.com/-b2_l0-EJukI/XxUkcsQ8Q6I/AAAAAAAAHRo/8QYsTD4qWQ0mh53sldAbXJOEa3XhvpvPQCLcBGAsYHQ/w400-h221/5.png" width="400" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-sb_EgSu-1NY/XxUkeMkh_tI/AAAAAAAAHRs/BuCMLr_lAl0EyhSon6kzksQcTSptH2O4gCLcBGAsYHQ/s601/6.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="242" data-original-width="601" height="161" src="https://1.bp.blogspot.com/-sb_EgSu-1NY/XxUkeMkh_tI/AAAAAAAAHRs/BuCMLr_lAl0EyhSon6kzksQcTSptH2O4gCLcBGAsYHQ/w400-h161/6.png" width="400" /></a></div><div><br /></div><div>Lets add the AWS account we configure earlier as a variable in this project. Name it "AWS_Account" and in value section click on Open Editor and select as shown below.</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-jE61Vm4B1ao/XxUlQ6oosfI/AAAAAAAAHR0/rtDH1f1wsZcKyuIj5hgJXtTy-TyG6YcJgCLcBGAsYHQ/s1004/7.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="411" data-original-width="1004" height="256" src="https://1.bp.blogspot.com/-jE61Vm4B1ao/XxUlQ6oosfI/AAAAAAAAHR0/rtDH1f1wsZcKyuIj5hgJXtTy-TyG6YcJgCLcBGAsYHQ/w625-h256/7.png" width="625" /></a></div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div>Please select the Scope as the environment we created earlier. This will be the only varaible we intend to create for time being. Save the variable created.</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-iZdKmOBOtd0/XxUls-dR6xI/AAAAAAAAHR8/pc-OMlyYvy8SJTtcVAbLqEJM5arVBPalACLcBGAsYHQ/s1293/8.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="265" data-original-width="1293" height="130" src="https://1.bp.blogspot.com/-iZdKmOBOtd0/XxUls-dR6xI/AAAAAAAAHR8/pc-OMlyYvy8SJTtcVAbLqEJM5arVBPalACLcBGAsYHQ/w625-h130/8.png" width="625" /></a></div><div><br /></div><div>Its time to create deployment steps. Go to "Process" on left hand pane and click "Add Step" button.</div><div>Now choose "Terrafrom" category and "Apply terraform template" step as shon below.</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-SxyXPXOUzGA/XxUnFF8xUZI/AAAAAAAAHSI/iddazGNvRkw83xDdQBvJXtON2h_YoRwgwCLcBGAsYHQ/s1262/9.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="638" data-original-width="1262" height="318" src="https://1.bp.blogspot.com/-SxyXPXOUzGA/XxUnFF8xUZI/AAAAAAAAHSI/iddazGNvRkw83xDdQBvJXtON2h_YoRwgwCLcBGAsYHQ/w625-h318/9.png" width="625" /></a></div><div><br /></div><div>Give a meaningful name like "Terraform - Cretae AWS S3 Bucket". Leave other selections about "Once on a worker pool" and "Default worker pool" as we intent to provision once and using shared resources provided by Octopus cloud.</div><div><br /></div><div>Now select the AWS Account section as shown below. Select the Varaible we create assigning the AWS account. </div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-ttkZ_I2Lh_k/XxUoi6-oKOI/AAAAAAAAHSU/i2w6pn2fwvwGx9Fhrih2FDh3-B3SR2ByQCLcBGAsYHQ/s696/10.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="495" data-original-width="696" height="445" src="https://1.bp.blogspot.com/-ttkZ_I2Lh_k/XxUoi6-oKOI/AAAAAAAAHSU/i2w6pn2fwvwGx9Fhrih2FDh3-B3SR2ByQCLcBGAsYHQ/w625-h445/10.png" width="625" /></a></div><div><br /></div><div>In the template section select "Template Source" as "Source code" and paste the below code in it. Please change the highlighted part with a unique bucket name as per your likes.</div><div><pre>provider "aws" {
region="us-west-2"
}
resource "aws_s3_bucket" "<span style="background-color: white;">tf_demos3bucket</span>" {
bucket="<span style="background-color: #01ffff;">terrafromoctopusbucket</span>"
acl="private"
}</pre></div><div>Save the step and now click "Create release" button for and then click "Save" button. This action will create a new build with the current variables and deployment steps.</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-ZL7bIM4KqiA/XxUrT9_OJfI/AAAAAAAAHSg/xTWgwAqVoegn7SbUEsgvDUiiERPG-4NnACLcBGAsYHQ/s1002/11.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="296" data-original-width="1002" height="186" src="https://1.bp.blogspot.com/-ZL7bIM4KqiA/XxUrT9_OJfI/AAAAAAAAHSg/xTWgwAqVoegn7SbUEsgvDUiiERPG-4NnACLcBGAsYHQ/w625-h186/11.png" width="625" /></a></div><div><br /></div><div>Now click "Deploy to AWS_Development" to trigger our first deployment.</div><div><br /></div><div>Here is the out put in both Octopus and AWS.</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-ji3U09kACoM/XxUs8ajlUsI/AAAAAAAAHSs/ZqcGo77My54QyWF_UUSLHsjNcVN4mGgfQCLcBGAsYHQ/s986/14.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="612" data-original-width="986" height="389" src="https://1.bp.blogspot.com/-ji3U09kACoM/XxUs8ajlUsI/AAAAAAAAHSs/ZqcGo77My54QyWF_UUSLHsjNcVN4mGgfQCLcBGAsYHQ/w625-h389/14.png" width="625" /></a></div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-sVzwK3qyeas/XxUtVSFDBlI/AAAAAAAAHS0/DBy86IqhEM08nlEozdnKfsN1_uT2zFyXQCLcBGAsYHQ/s1289/15.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="461" data-original-width="1289" height="224" src="https://1.bp.blogspot.com/-sVzwK3qyeas/XxUtVSFDBlI/AAAAAAAAHS0/DBy86IqhEM08nlEozdnKfsN1_uT2zFyXQCLcBGAsYHQ/w625-h224/15.png" width="625" /></a></div><div><br /></div><div>Now lets look at how to destroy the S3 bucket we created using Octopus. </div><div><br /></div><div><b>Step 5: </b> Create a second step in our project with name "Terraform - Delete AWS S3 bucket" and seect the category and template as shown below.</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-Gd9iTmE6cU8/XxUuus0PrqI/AAAAAAAAHTA/MxY1miG35I8srQdtCR79QEGWFMR0iJwFgCLcBGAsYHQ/s720/16.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="561" data-original-width="720" height="389" src="https://1.bp.blogspot.com/-Gd9iTmE6cU8/XxUuus0PrqI/AAAAAAAAHTA/MxY1miG35I8srQdtCR79QEGWFMR0iJwFgCLcBGAsYHQ/w500-h389/16.png" width="500" /></a></div><div><br /></div><div>Now in the details section, repeat what you have done for Create step(first step), including selection of AWS account and Source code we used. You can copy it from above. Save the new step and disable "Terraform - Create S3 Bucket"(First step).</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-IT-1cbyQ1Ps/XxUwZ3uJqGI/AAAAAAAAHTM/TUvEOjQQkqcn3ESigeeQfLsSob0f_jxRQCLcBGAsYHQ/s1278/17.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="327" data-original-width="1278" height="161" src="https://1.bp.blogspot.com/-IT-1cbyQ1Ps/XxUwZ3uJqGI/AAAAAAAAHTM/TUvEOjQQkqcn3ESigeeQfLsSob0f_jxRQCLcBGAsYHQ/w625-h161/17.png" width="625" /></a></div><div><br /></div><div>Save and create release and use "Deploy to AWS_Development" button to deploy it. Now it should delete AWS S3 bucket as per our confirguration. Here is the output.</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-2L-9Pzb12yE/XxUzpepIxDI/AAAAAAAAHTY/RZUpvyzbmbsWubQbDDUDJJo-KfMRkCahQCLcBGAsYHQ/s990/18.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="593" data-original-width="990" height="375" src="https://1.bp.blogspot.com/-2L-9Pzb12yE/XxUzpepIxDI/AAAAAAAAHTY/RZUpvyzbmbsWubQbDDUDJJo-KfMRkCahQCLcBGAsYHQ/w625-h375/18.png" width="625" /></a></div><div><br /></div><p style="text-align: left;">Now don't get deceived by green color of this step and "Destory Complete !" message. Look at enitre message "<span style="background-color: #fcff01; color: #333333; font-family: consolas, menlo, monaco, "courier new", monospace; font-size: 14px; letter-spacing: 0.25px; white-space: pre-wrap;">Destroy complete! Resources: 0 destroyed.</span>" </p><p style="text-align: left;">This says nothing has been deleted. If you check my <a href="https://pratapreddypilaka.blogspot.com/2020/07/iac-infrastructure-as-code-using.html" target="_blank">earlier article about basics</a>, both apply and destory commands were sucessfull with same configuration. What happened this time.</p><h4 style="text-align: left;">Now lets talk about state file in terraforms. </h4><p>Terraform must store state about your managed infrastructure and configuration. This state is used by Terraform to map real world resources to your configuration, keep track of metadata, and to improve performance for large infrastructures.<br /><br />This state is stored by default in a local file if you are running bash commands form a local machine. </p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-8BVH1T70gqc/XxU1-RkHDWI/AAAAAAAAHTk/2NAcvl33XFMaoAAC4jWYPxHSJIJuflkxQCLcBGAsYHQ/s816/19.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="216" data-original-width="816" height="166" src="https://1.bp.blogspot.com/-8BVH1T70gqc/XxU1-RkHDWI/AAAAAAAAHTk/2NAcvl33XFMaoAAC4jWYPxHSJIJuflkxQCLcBGAsYHQ/w625-h166/19.png" width="625" /></a></div><p>But Octopus do need a remote state file to access the current state of the infrastructure. Since we didn't consider the remote state file, Octopus cannot find any state file to determine its sate, so it dint see the newly created S3 bcuket. SO it gave a message saying "0 to destroy".</p><p><b>Step 6:</b>Now we need to delete the S3 bucket manually and change the "source code" in both create and delete steps to code shown below. </p><p></p><pre><span style="background-color: #01ffff;">terraform {
backend "s3" {
bucket = "</span><span style="background-color: #fcff01;">terrafromoctopusstatebucket</span><span style="background-color: #01ffff;">"
key = "Octopus S3 Operations"
region = "us-west-2"
}
}</span>
provider "aws" {
region="us-west-2"
}
resource "aws_s3_bucket" "tf_demos3bucket" {
bucket="terrafromoctopusbucket"
acl="private"
}</pre><p></p><p>Now this source code says, create or reffer to a remote state file on AWS S3 bucket. We exclusively need to create a bucket to maintain all our state files related to all our terraform projects.</p><p>In this case we name it "terrafromoctopusstatebucket". So we manually need to create a S3 bucket with this name. I created it and you can see its empty now.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-uFusmPUk9W0/XxU5Sq4i2FI/AAAAAAAAHTw/dGv8kKyzAgYmGqoXqYCMlO_FDqzEjHlDwCLcBGAsYHQ/s1076/20.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="400" data-original-width="1076" height="233" src="https://1.bp.blogspot.com/-uFusmPUk9W0/XxU5Sq4i2FI/AAAAAAAAHTw/dGv8kKyzAgYmGqoXqYCMlO_FDqzEjHlDwCLcBGAsYHQ/w625-h233/20.png" width="625" /></a></div><p>Now after updating both create and delete steps with the "Source Code" shown above, Enable the create step and disable the delete step. Create a new release and deploy it.</p><p>This will create S3 bucket and create a state file on the S3 baucket names "terrafromoctopusstatebucket". Lets check it.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-VSBy_VjkLKs/XxU6fsy2zYI/AAAAAAAAHT8/jgAWxz9oQS0KQNlRvdCwgpOw6VbwZIFYwCLcBGAsYHQ/s1007/21.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="655" data-original-width="1007" height="406" src="https://1.bp.blogspot.com/-VSBy_VjkLKs/XxU6fsy2zYI/AAAAAAAAHT8/jgAWxz9oQS0KQNlRvdCwgpOw6VbwZIFYwCLcBGAsYHQ/w625-h406/21.png" width="625" /></a></div><p>Look at the remote state file created on S3 bucket and compare it with the time stamp from creation shown above.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-Aq0M2sw_8Ag/XxU6_2FPqbI/AAAAAAAAHUE/Yf5JRBXC4hIC7qvOjLVFapySZ6GwxJeLQCLcBGAsYHQ/s1302/22.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="370" data-original-width="1302" height="179" src="https://1.bp.blogspot.com/-Aq0M2sw_8Ag/XxU6_2FPqbI/AAAAAAAAHUE/Yf5JRBXC4hIC7qvOjLVFapySZ6GwxJeLQCLcBGAsYHQ/w625-h179/22.png" width="625" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><p>Bucket created in AWS.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-iERttYksfr4/XxU7yghV7CI/AAAAAAAAHUY/qaWSzKnRkZkCjZI9vCfLAuUS455VaccxwCLcBGAsYHQ/s1310/23.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="190" data-original-width="1310" height="91" src="https://1.bp.blogspot.com/-iERttYksfr4/XxU7yghV7CI/AAAAAAAAHUY/qaWSzKnRkZkCjZI9vCfLAuUS455VaccxwCLcBGAsYHQ/w625-h91/23.png" width="625" /></a></div><p>Step 7: Now edit the project , disable Create step and enable Delete step. Save the build and "Create a new release" and deploy it. Now when we deploy the build, it will try to delete the S3 bucket this time.</p><p>Here is the output on Octopu deployment.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-dEKxtD80D6I/XxU9lamlqpI/AAAAAAAAHUk/hCReF85YqnwF_xxGNjxRaAwl7gJdTYn8ACLcBGAsYHQ/s1081/24.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="662" data-original-width="1081" height="383" src="https://1.bp.blogspot.com/-dEKxtD80D6I/XxU9lamlqpI/AAAAAAAAHUk/hCReF85YqnwF_xxGNjxRaAwl7gJdTYn8ACLcBGAsYHQ/w625-h383/24.png" width="625" /></a></div><p>Here is the statefile getting updated, please compare the time stamp from above screen for deletion step.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-15Rl52t3pfQ/XxU-BQlASbI/AAAAAAAAHUs/JT3cg9S8UD85E2Dt_9If8eMmNf5mN6d0ACLcBGAsYHQ/s1334/25.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="415" data-original-width="1334" height="195" src="https://1.bp.blogspot.com/-15Rl52t3pfQ/XxU-BQlASbI/AAAAAAAAHUs/JT3cg9S8UD85E2Dt_9If8eMmNf5mN6d0ACLcBGAsYHQ/w625-h195/25.png" width="625" /></a></div><p>And lastly the S3 bucket "terrafromoctopusbucketket" was sucessfully deleted as intended.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-PIDUdWxgZ0Q/XxU-fWDVmJI/AAAAAAAAHU4/yyCWO0agGVsXX8ARrcK_WK3KjKAcciB0QCLcBGAsYHQ/s1322/26.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="456" data-original-width="1322" height="216" src="https://1.bp.blogspot.com/-PIDUdWxgZ0Q/XxU-fWDVmJI/AAAAAAAAHU4/yyCWO0agGVsXX8ARrcK_WK3KjKAcciB0QCLcBGAsYHQ/w625-h216/26.png" width="625" /></a></div><p>Finally we were able to provision and destroy the Infrastructure using Terrafrom and Octopus integration. </p><p>Please note that just by updating the "Source code" in both the Create and Delete steps, you can create any infrastructure on any cloud platform. Remeber terrafrom is could-agnostic.</p>Pratap Reddy Pilakahttp://www.blogger.com/profile/17141976956878163883noreply@blogger.com0tag:blogger.com,1999:blog-4208378482021219922.post-24188500560964395322020-07-15T01:40:00.002-07:002020-07-18T15:33:44.253-07:00IaC "Infrastructure as Code" using Terraform - Basics<p style="text-align: left;">Infrastucture as Code became a critical part of Devops. Some of the Infrastructure management tools that we know are CloudFormation famous for AWS, ARM for Azure, CDM for Google cloud platforms.</p><div>Terraform is a IaC tool which can translate HCL (Hasi Corp Language) to API calls which can be interpreted by various cloud platforms. Ths making it cloud-agnostic tool.</div><div><br /></div><div>Another reason for using Terraform is it has a planning step when compared to other IaC tools. We will look at that feature in this article further down.</div><div><br /></div><div>CloudFormation(AWS), ARM(Azure) and CDM(Google) are firstparty IaC tools, this means they will handle all infrastructure management tasks specific to that cloud platform. But these tools are not suitable for most of the thirt party service integration. Terraform addresses this issue with cloud-agnostic and better support for thirdparty service integration.</div><div><br /></div><div>For more details please visit <a href="https://www.terraform.io/">https://www.terraform.io/</a></div><div><br /></div><a name='more'></a><div>Enough with theory lets jum in to practice and do some code, wait. . . . Infrastructure as Code. </div><div><br /></div><div>Here i will be targeting AWS for now as i have bit of experince with it.</div><div><br /></div><div>We will quickly install required tools and then perform a quick tasks like create, delete, plan infrasturcture change tasks then we will discuss them in detail.</div><div><br /></div><div>First download and install below tools:</div><div><br /></div><div>1. Git - try this link <a href="https://git-scm.com/download/win">https://git-scm.com/download/win</a></div><div>2. Terraform - try this link <a href="https://www.terraform.io/downloads.html">https://www.terraform.io/downloads.html</a></div><div>3. AWS Free-Tier subscription. Install AWS CLI on your machine.</div><div><br /></div><div>You can see that terraform is a single executable file and i will show how we can use it.</div><div><br /></div><div>I assume you downloaded and installed Git which will create a local git repo and will install Git GUI amd Git bash. We will use Gt Bash here.</div><div><br /></div><div>Setting up Terraform:</div><div>Create a bin file in "C:\Users\xxUserNamexx\" and copy the executable to that bin folder.</div><div>Now open edit envirnment variables window, either form system properties or from run.</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-waRF_IwJrL8/Xw6XKLCOfoI/AAAAAAAAHMs/b5vg3PWCF9EgqLt5LfDz7Jt8QqnzDBiBQCLcBGAsYHQ/s655/2.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="388" data-original-width="655" height="298" src="https://1.bp.blogspot.com/-waRF_IwJrL8/Xw6XKLCOfoI/AAAAAAAAHMs/b5vg3PWCF9EgqLt5LfDz7Jt8QqnzDBiBQCLcBGAsYHQ/w500-h298/2.png" width="500" /></a></div><div><br /></div><div>Now "terraform" will be a executable shell command. You can check by running terraform in any shell.</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-fBOOpQCpc8A/Xw6Xp3oO1vI/AAAAAAAAHM0/x6GKEO_IXx02fuDn57mOfB_jjO7tepmjwCLcBGAsYHQ/s572/3.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="252" data-original-width="572" height="275" src="https://1.bp.blogspot.com/-fBOOpQCpc8A/Xw6Xp3oO1vI/AAAAAAAAHM0/x6GKEO_IXx02fuDn57mOfB_jjO7tepmjwCLcBGAsYHQ/w625-h275/3.png" width="625" /></a></div><div><br /></div><h3 style="text-align: left;">Task: Lets create a AWS S3 bucket using terraform.</h3><div>Why you ask? It is the easiest AWS action you can perform. </div><div><br /></div><div>Once you install AWS CLI, Open AWS console in browser and go to IAM.</div><div><br /></div><div>Create a user "terraform" with "Adminstrator" access. Please note that this is not a ideal Prod scenario and you should never assign "Adminstrator" permissions to any IAM Users. We just want to keep things simple for our Demo.</div><div><br /></div><div>Don't forget to download the credetials CSV bfore you click that close button.</div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-0TngJGdbgOc/Xw6h6kxv4ZI/AAAAAAAAHNw/z0ZWJtR-vwY2KLeKwwAeSMxJs4fxi5wbACLcBGAsYHQ/s994/9.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="410" data-original-width="994" height="258" src="https://1.bp.blogspot.com/-0TngJGdbgOc/Xw6h6kxv4ZI/AAAAAAAAHNw/z0ZWJtR-vwY2KLeKwwAeSMxJs4fxi5wbACLcBGAsYHQ/w625-h258/9.png" width="625" /></a></div><div>This CSV will have both Access key ID and Secret Access key. Copy those values for further use.</div><div><br /></div><div>Go to "C:\Users\xxUserNamexx\" and you can see a folder ".aws" and inside this folder create a file called "credetials" with no file extension. Like this.</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-vI7-trUL7_s/Xw6yV2S7xmI/AAAAAAAAHN8/LkezhwZGEW0lXpzmkKt6osgGS-7B8hdwACLcBGAsYHQ/s455/8.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="181" data-original-width="455" height="159" src="https://1.bp.blogspot.com/-vI7-trUL7_s/Xw6yV2S7xmI/AAAAAAAAHN8/LkezhwZGEW0lXpzmkKt6osgGS-7B8hdwACLcBGAsYHQ/w400-h159/8.png" width="400" /></a></div><div><br /></div><div>The content of the credentials file be like </div><div><br /></div><div><pre>[default]
aws_access_key_id = XXXXXXXXXXXXXXXXXXXXXXXXXB
aws_secret_access_key = xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxq</pre></div><div>Please use the Access Sey ID and the Secret Access key you saved from CSV file earlier.</div><div><br /></div><div>Now when ever there is a request to access AWS, they will be executed under the credentials of "terraform" user.</div><div><br /></div><div>Now lets move to Git.</div><div><br /></div><div>Open Git bash, and run "git init". This will initialise local git repo and will give sucessfull message like this. Sometimes, if ou are using it for the first time, it may ask you to chnage the config to update emailaddress and username. But dont worry, it will also give the command to execute for those tasks.</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-IYH_rJ_kYK4/Xw6ZCjmIIOI/AAAAAAAAHNA/oh2Uum_lidIIK2FZTzUsxdJq_1xWzFXPQCLcBGAsYHQ/s579/4.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="178" data-original-width="579" height="193" src="https://1.bp.blogspot.com/-IYH_rJ_kYK4/Xw6ZCjmIIOI/AAAAAAAAHNA/oh2Uum_lidIIK2FZTzUsxdJq_1xWzFXPQCLcBGAsYHQ/w625-h193/4.png" width="625" /></a></div><div> </div><div>Lets create our first terraform code file, but lets do that in a demo folder in %userprofile% folder.</div><div> </div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-RH8ZjxJH54g/Xw6Z-p1la6I/AAAAAAAAHNM/cYPhEe24UmI_v3KgA97yiyyN6Z13cNXPACLcBGAsYHQ/s580/5.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="231" data-original-width="580" height="249" src="https://1.bp.blogspot.com/-RH8ZjxJH54g/Xw6Z-p1la6I/AAAAAAAAHNM/cYPhEe24UmI_v3KgA97yiyyN6Z13cNXPACLcBGAsYHQ/w625-h249/5.png" width="625" /></a></div><div><br /></div><div>Last command is to create "terraform_s3.tf" file under demo folder. this will create/open file for edit.</div><div>Press "i" to enter edit mode which you can see at bottom of the window.</div><div>Paste this code and press "esc" and type :wq and enter.</div><div>You can see ":wq" command at the bottom of the edit window.</div><div><pre>provider "aws" {
profile="default"
region="us-west-2"
}
resource "aws_s3_bucket" "tf_training" {
bucket="prataptfdemos3"
acl="private"
}</pre></div><div>That should look like this. Lets understand what we are trying to do here. We are creating a AWS S3 bucket with name "prataptfdemos3".</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-rOGXeew7JyY/Xw6cS7EbzcI/AAAAAAAAHNY/MOk0oy-jyIQYfw8TghCFaub4GbvN2wxVgCLcBGAsYHQ/s581/6.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="230" data-original-width="581" height="249" src="https://1.bp.blogspot.com/-rOGXeew7JyY/Xw6cS7EbzcI/AAAAAAAAHNY/MOk0oy-jyIQYfw8TghCFaub4GbvN2wxVgCLcBGAsYHQ/w625-h249/6.png" width="625" /></a></div><div><br /></div><div>Now the editor window will be closed and you will be back on main bash window. </div><div>Run "git add terraform_s3.tf" then run "git commit" commands.</div><div>For commit, you can either give comment or press "esc" folloswed by "":wq" + enter to finish commit.</div><div><br /></div><div>Terraform code file creation is finished and now its time to run it.</div><div><br /></div><div>Start with "terrafrom init" command. That start something like this.</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-an5u8lsuGGc/Xw6eRtSWk5I/AAAAAAAAHNk/Um1awfPvGeQQITzs40n9keFpFh5XOTixwCLcBGAsYHQ/s581/7.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="230" data-original-width="581" height="249" src="https://1.bp.blogspot.com/-an5u8lsuGGc/Xw6eRtSWk5I/AAAAAAAAHNk/Um1awfPvGeQQITzs40n9keFpFh5XOTixwCLcBGAsYHQ/w625-h249/7.png" width="625" /></a></div><div><br /></div><div>Now understand next command before execution. It is <b>"terraform apply".</b></div><div>This means you are requesting terraform to create infrastructure , in our case a S3 bucket in AWS using "terraform" user permissisons.</div><div> </div><div>Lets execute the command and see what happens.</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-f5GNG95D1zQ/Xw62uwGNPZI/AAAAAAAAHOU/pjwMfFPFhLMjjH16qHIClCsZhsBpjStYgCLcBGAsYHQ/s583/10.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="583" data-original-width="440" height="781" src="https://1.bp.blogspot.com/-f5GNG95D1zQ/Xw62uwGNPZI/AAAAAAAAHOU/pjwMfFPFhLMjjH16qHIClCsZhsBpjStYgCLcBGAsYHQ/w593-h781/10.png" width="593" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div>Lets understand what it is saying. Terraform will generate a plan of action and show us to verify and confirm the actions it is going to perform. In our case its saying that we have requested to add a resource which is of type S3 bucket with name "prataptfdemos3". </div><div><br /></div><div>Now type "yes" to continue or "no" to exit.</div><div><br /></div><div>Once commited , her eis the output.</div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-ZFSD3q0OW3I/Xw63--0HcHI/AAAAAAAAHOg/dh4Fnfe0nQ09HbnAUlIDjtGIyaJQ2icogCLcBGAsYHQ/s540/11.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="169" data-original-width="540" height="195" src="https://1.bp.blogspot.com/-ZFSD3q0OW3I/Xw63--0HcHI/AAAAAAAAHOg/dh4Fnfe0nQ09HbnAUlIDjtGIyaJQ2icogCLcBGAsYHQ/w625-h195/11.png" width="625" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-91HzxwO_z38/Xw66AOuCrpI/AAAAAAAAHPA/bCG8UMQriRA5gYPTdDN75W2PW57O0ev2ACLcBGAsYHQ/s902/12.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="491" data-original-width="902" height="341" src="https://1.bp.blogspot.com/-91HzxwO_z38/Xw66AOuCrpI/AAAAAAAAHPA/bCG8UMQriRA5gYPTdDN75W2PW57O0ev2ACLcBGAsYHQ/w625-h341/12.png" width="625" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div> </div><div>Here is the AWS S3 bucket created using terraform. </div><div><br /></div><div>Lets move to next command in terraform. ""terraform destroy". This command deleted the infrastructure which is menioned in "terrafrom_s3.tf" file.</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-1lAjpmg4YnI/Xw641gZRfsI/AAAAAAAAHO0/vbn_djo3P30YzjcYTeWMWV6CVypKYQClQCLcBGAsYHQ/s592/13.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="592" data-original-width="579" height="625" src="https://1.bp.blogspot.com/-1lAjpmg4YnI/Xw641gZRfsI/AAAAAAAAHO0/vbn_djo3P30YzjcYTeWMWV6CVypKYQClQCLcBGAsYHQ/w611-h625/13.png" width="611" /></a></div><div><br /></div><div>terraform has generated the plan and asking us that we have requested to delete the S3 bucket with name "prataptfdemos3". Type "yes" to continue or "no" to exit.</div><div> Here is the out put if you type yes.</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-tlcLxjesiK4/Xw66FHtpMcI/AAAAAAAAHPE/R3R8BqxpbLsDdWy3ifmGtmhX7LLBaBG1wCLcBGAsYHQ/s440/14.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="160" data-original-width="440" height="226" src="https://1.bp.blogspot.com/-tlcLxjesiK4/Xw66FHtpMcI/AAAAAAAAHPE/R3R8BqxpbLsDdWy3ifmGtmhX7LLBaBG1wCLcBGAsYHQ/w625-h226/14.png" width="625" /></a></div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-cfZtue39Ka0/Xw66iKC8iiI/AAAAAAAAHPQ/Y8SMyZpk4rIQmo7yMhmbbi3Hv05JehO8ACLcBGAsYHQ/s873/15.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="503" data-original-width="873" height="360" src="https://1.bp.blogspot.com/-cfZtue39Ka0/Xw66iKC8iiI/AAAAAAAAHPQ/Y8SMyZpk4rIQmo7yMhmbbi3Hv05JehO8ACLcBGAsYHQ/w625-h360/15.png" width="625" /></a></div><div><br /></div><div>Now you can see that the AWS S3 bucket has been deleted as we intended. </div><div><br /></div><div>Lets check the next command <b>"terraform plan"</b>. This command will not either create or delete any infrastructure, but will generate a plan of execution. This plan can be used either to create or delete operations. You can save the plan as a file. Here in the below screen, i have saved the plan as a file "awss3.pln"</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-ujvCmtWWnGI/Xw68AwfLO3I/AAAAAAAAHPc/r9zyOiBh-asCpM2r8PKt9_cEv_LLpUUFQCLcBGAsYHQ/s737/16.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="737" data-original-width="552" height="781" src="https://1.bp.blogspot.com/-ujvCmtWWnGI/Xw68AwfLO3I/AAAAAAAAHPc/r9zyOiBh-asCpM2r8PKt9_cEv_LLpUUFQCLcBGAsYHQ/w586-h781/16.png" width="586" /></a></div><div><br /></div><div>The plan shows that we are requesting to create a new S3 bucket. Now lets see how we can use this plan to implement the Infrastructure change.</div><div><br /></div><div>To use the plan, we need to execute "terraform apply awss3.pln".</div><div>This now implements the plan, in this case to create a S3 bucket.</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-9E2nHfeXjuc/Xw69iLeI7LI/AAAAAAAAHPo/bgQn0PfhcxoxQAcd1L4qIAClOLx2FtI0wCLcBGAsYHQ/s539/17.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="258" data-original-width="539" height="299" src="https://1.bp.blogspot.com/-9E2nHfeXjuc/Xw69iLeI7LI/AAAAAAAAHPo/bgQn0PfhcxoxQAcd1L4qIAClOLx2FtI0wCLcBGAsYHQ/w625-h299/17.png" width="625" /></a></div><div><br /></div><div>There is no harm in applying the plan again. As it is already implemented, terraform will give an error saying this plan is stale and cannot be executed.</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-40Ov3gTJEJ4/Xw6-AbdaABI/AAAAAAAAHPw/Af3oqdszfDEhnMe5NPj5eZgzSSik4Uq_QCLcBGAsYHQ/s545/18.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="176" data-original-width="545" height="201" src="https://1.bp.blogspot.com/-40Ov3gTJEJ4/Xw6-AbdaABI/AAAAAAAAHPw/Af3oqdszfDEhnMe5NPj5eZgzSSik4Uq_QCLcBGAsYHQ/w625-h201/18.png" width="625" /></a></div><div><br /></div><div><br /></div><div>Lastly lets generate the plan to destroy. "terraform plan -destroy -out awss3delete.pln"</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-nbQX-39nXuw/Xw6-mDtA84I/AAAAAAAAHP8/zUI1lZkA2v0hru-TLzl8uRVBPsUdB-mVACLcBGAsYHQ/s714/19.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="714" data-original-width="577" height="781" src="https://1.bp.blogspot.com/-nbQX-39nXuw/Xw6-mDtA84I/AAAAAAAAHP8/zUI1lZkA2v0hru-TLzl8uRVBPsUdB-mVACLcBGAsYHQ/w633-h781/19.png" width="633" /></a></div><div><br /></div><div>We can now implement the plan of detion by applying the "awss3delete.pln".</div><div>Command to do that is "terraform apply awss3delete.pln"</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-MqyeMcsVpEI/Xw6_R0EOFdI/AAAAAAAAHQE/MW7P8Apg9KscFf9GqJ5GxRio7OWabGBAACLcBGAsYHQ/s442/20.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="166" data-original-width="442" height="235" src="https://1.bp.blogspot.com/-MqyeMcsVpEI/Xw6_R0EOFdI/AAAAAAAAHQE/MW7P8Apg9KscFf9GqJ5GxRio7OWabGBAACLcBGAsYHQ/w625-h235/20.png" width="625" /></a></div><div><br /></div><div><br /></div><div>With this we have covered the basics. </div><div><br /></div><div>In next aritcle we will automate the same using Octopus deploy. Here is a taste of it.</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-CJRTL2oanu4/Xw7Am_cFuWI/AAAAAAAAHQY/bfaoZ4OLLBwTCF6zFIIubDslwYbyRaZ2wCLcBGAsYHQ/s969/21.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="523" data-original-width="969" height="338" src="https://1.bp.blogspot.com/-CJRTL2oanu4/Xw7Am_cFuWI/AAAAAAAAHQY/bfaoZ4OLLBwTCF6zFIIubDslwYbyRaZ2wCLcBGAsYHQ/w625-h338/21.png" width="625" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div><div> </div><div><br /></div>Pratap Reddy Pilakahttp://www.blogger.com/profile/17141976956878163883noreply@blogger.com0tag:blogger.com,1999:blog-4208378482021219922.post-59474176009975061542020-02-18T23:46:00.002-08:002020-02-19T00:19:48.626-08:00Power Apps - Test Studio - Automation Testing<div dir="ltr" style="text-align: left;" trbidi="on">
At last we got feature of automated testing, improving "Application Life Cycle Management" for Power Apps. (Watch in Full Screen)<br />
<div class="separator" style="clear: both; text-align: center;">
<iframe allowfullscreen='allowfullscreen' webkitallowfullscreen='webkitallowfullscreen' mozallowfullscreen='mozallowfullscreen' width='600' height='300' src='https://www.blogger.com/video.g?token=AD6v5dz-6mfqx-7LXO8hd5YuYnkcqHseMxD9zyBSHVipSfFKwd-gw1unoWaczm41k6moVYz6KZQTA8_Kj-5GrBShyw' class='b-hbp-video b-uploaded' frameborder='0'></iframe></div>
<br />
<br />
In this post we will learn how we will use Test Studio for Power Apps.<br />
<a name='more'></a><br />
<br />
I have created a simple power app and there are 3 screens, most of them are self explanatory.<br />
<br />
<b>Home Screen:</b> With 2 buttons to navigate to either "New User" Screen or "Search User" screen.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-0FliS13jjHE/XkzbdPmb1CI/AAAAAAAAHF8/soCL_QSfizAILZU6urPklfiIInHLWUCDgCLcBGAsYHQ/s1600/1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="243" data-original-width="596" height="129" src="https://1.bp.blogspot.com/-0FliS13jjHE/XkzbdPmb1CI/AAAAAAAAHF8/soCL_QSfizAILZU6urPklfiIInHLWUCDgCLcBGAsYHQ/s320/1.png" width="320" /></a></div>
<b>New User Screen: </b>An Edit form with 4 fields and data is saved in a Sharepoint online List.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-bUvgbZYBBZg/XkzbzdVXW2I/AAAAAAAAHGE/BBmGFw6h3Y8T19LBiZQFq-hrTW98M_tggCLcBGAsYHQ/s1600/2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="465" data-original-width="824" height="360" src="https://1.bp.blogspot.com/-bUvgbZYBBZg/XkzbzdVXW2I/AAAAAAAAHGE/BBmGFw6h3Y8T19LBiZQFq-hrTW98M_tggCLcBGAsYHQ/s640/2.png" width="640" /></a></div>
<br />
<b>Search User Screen:</b> Screen with Search Box which retrieves data from Sharepoint Online list. If Serach box is empty , it will display an error message.<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-dDHUXJDFJvw/XkzcKl5Z7pI/AAAAAAAAHGM/5rBa_PHcR2UFF1T2v4idf0xLm0oNcagKgCLcBGAsYHQ/s1600/4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="241" data-original-width="708" height="135" src="https://1.bp.blogspot.com/-dDHUXJDFJvw/XkzcKl5Z7pI/AAAAAAAAHGM/5rBa_PHcR2UFF1T2v4idf0xLm0oNcagKgCLcBGAsYHQ/s400/4.png" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-iXpWqYYBfww/XkzcKsPgBtI/AAAAAAAAHGQ/KKUAe-PUVLQWWHBzp8oNlMFM29_WTGS2ACEwYBhgL/s1600/3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="677" data-original-width="1123" height="240" src="https://1.bp.blogspot.com/-iXpWqYYBfww/XkzcKsPgBtI/AAAAAAAAHGQ/KKUAe-PUVLQWWHBzp8oNlMFM29_WTGS2ACEwYBhgL/s400/3.png" width="400" /></a></div>
<br />
Test Studio can be accessed from the "Home" tab, in "Advanced Tools", select "Open Tests" option.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-2NQycaU8ZQ4/XkzdfarF3hI/AAAAAAAAHGg/kauAB7wbuC8hHnP__Z0J6MkKUv71GewTACLcBGAsYHQ/s1600/5.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="506" data-original-width="929" height="347" src="https://1.bp.blogspot.com/-2NQycaU8ZQ4/XkzdfarF3hI/AAAAAAAAHGg/kauAB7wbuC8hHnP__Z0J6MkKUv71GewTACLcBGAsYHQ/s640/5.png" width="640" /></a></div>
<br />
First we need to get familiar with some terminology.<br />
<br />
<b>Test Step:</b> An action performed on power app (eg: Clicking a button or Setting a value in text box)<br />
<br />
<b>Test Case: </b>Collection of steps to perform a specific task. (eg: Clicking a button and navigating to desired screen or Entering data required and submitting it to data source by a button click.)<br />
<br />
<b>Test Suite: </b>Collection of test cases, i would recommend Create all the test cases pertaining to a Screen under one Test Suite.<br />
<br />
Lets start with a Test Suite "Home Screen". I have created 2 Test cases one for each button click.<br />
<br />
When you create a new test case, this is what you see initially.<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-zqrAYd8qprM/XkzlHekQ3kI/AAAAAAAAHGw/7tP9p684e5gqBy7ZDOak8zZYMt3olfZNACLcBGAsYHQ/s1600/8.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="502" data-original-width="1314" height="244" src="https://1.bp.blogspot.com/-zqrAYd8qprM/XkzlHekQ3kI/AAAAAAAAHGw/7tP9p684e5gqBy7ZDOak8zZYMt3olfZNACLcBGAsYHQ/s640/8.png" width="640" /></a></div>
<br />
Click the Record button highlighted and start performing the actions on your powerapp and it will record every step you do.<br />
<br />
Please note that its better you need to take care of a few things on your own.<br />
<br />
#1 Not every test case should begin from Landing page, so you need to write step 1 for navigating to desired page.<br />
#2. All the actions like entering data, clicking buttons and navigating to different page will be registered.<br />
#3. Now you need to write the code for checks in form of Assert() method.<br />
<br />
Look at these screen shots and for better understanding.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-xyA6WAU_k7k/XkzmklNH8SI/AAAAAAAAHHA/QRwnl2k4_IIqVPgvzv8VTxLVjnT9IzuTwCLcBGAsYHQ/s1600/6.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="634" data-original-width="1396" height="290" src="https://1.bp.blogspot.com/-xyA6WAU_k7k/XkzmklNH8SI/AAAAAAAAHHA/QRwnl2k4_IIqVPgvzv8VTxLVjnT9IzuTwCLcBGAsYHQ/s640/6.png" width="640" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/--B1b9_OyjUo/Xkzmq8TE-zI/AAAAAAAAHHE/-RnSqAsnPn8Mzy3UcfK62K44aoWhw-0sACLcBGAsYHQ/s1600/7.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="643" data-original-width="1418" height="290" src="https://1.bp.blogspot.com/--B1b9_OyjUo/Xkzmq8TE-zI/AAAAAAAAHHE/-RnSqAsnPn8Mzy3UcfK62K44aoWhw-0sACLcBGAsYHQ/s640/7.png" width="640" /></a></div>
<br />
"Save" and "Publish" the Test Suite. Now select the test Suite and click on "Play" button. This is what you see. (Please watch in full screen)<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<iframe allowfullscreen='allowfullscreen' webkitallowfullscreen='webkitallowfullscreen' mozallowfullscreen='mozallowfullscreen' width='600' height='300' src='https://www.blogger.com/video.g?token=AD6v5dxOUGU413-p-IxUFqg2CzoCgBqMbWUaLEClXf5CtrZfAvjSLuOW_ySo6QPzJ8CH3DpQ1y2VltbMYh3kN3RnzQ' class='b-hbp-video b-uploaded' frameborder='0'></iframe></div>
<br />
Now Lets deal with Serach screen. Here are the screenshots of what we registred for test cases.<br />
<br />
Empty Search resulting in Error message:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-YsoFrEKSraA/XkzpN8MdBCI/AAAAAAAAHHo/RO2JRrFMLm412VhoDHeFPQDURratuJTdgCLcBGAsYHQ/s1600/9.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="527" data-original-width="1239" height="272" src="https://1.bp.blogspot.com/-YsoFrEKSraA/XkzpN8MdBCI/AAAAAAAAHHo/RO2JRrFMLm412VhoDHeFPQDURratuJTdgCLcBGAsYHQ/s640/9.png" width="640" /></a></div>
<br />
Search with proper data and then see if it fetched any results:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-7ik0r8F_nSs/XkzpWNIDu2I/AAAAAAAAHHs/X3YaGjszdPc7j_i9MeEo_YYbjlYiOGkuQCLcBGAsYHQ/s1600/10.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="565" data-original-width="1229" height="294" src="https://1.bp.blogspot.com/-7ik0r8F_nSs/XkzpWNIDu2I/AAAAAAAAHHs/X3YaGjszdPc7j_i9MeEo_YYbjlYiOGkuQCLcBGAsYHQ/s640/10.png" width="640" /></a></div>
<br />
Now here is the Test Suite run for you. (Please watch in full screen)<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<iframe allowfullscreen='allowfullscreen' webkitallowfullscreen='webkitallowfullscreen' mozallowfullscreen='mozallowfullscreen' width='600' height='300' src='https://www.blogger.com/video.g?token=AD6v5dzKB7iFHHSud0L7QJZ4JzGIpJlQQXYcBb0V2wfqbAYOem3o30S18fFi1suJTUpNePrDGmrZZrKApg-MwlacBg' class='b-hbp-video b-uploaded' frameborder='0'></iframe></div>
<br />
Lets create test suite for Create New user screen, but this requires use of two screens.<br />
<br />
First go to New user screen , enter all the data and then submit, then go to search screen and search for the newly created user.<br />
Record the steps, then modify the values as shown below to make it easy for automation. Now each test will use a different values on the forms.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-tMr2WIA7ryk/XkzrPuZI-lI/AAAAAAAAHH8/9O0qOr1Qq44HQJH2F6Ks-GlDSxDYSs5ywCLcBGAsYHQ/s1600/11.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="541" data-original-width="1532" height="226" src="https://1.bp.blogspot.com/-tMr2WIA7ryk/XkzrPuZI-lI/AAAAAAAAHH8/9O0qOr1Qq44HQJH2F6Ks-GlDSxDYSs5ywCLcBGAsYHQ/s640/11.png" width="640" /></a></div>
<br />
Now let me show you the Test run for create and then serach for new user. (Watch in Full Screen)<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<iframe allowfullscreen='allowfullscreen' webkitallowfullscreen='webkitallowfullscreen' mozallowfullscreen='mozallowfullscreen' width='600' height='300' src='https://www.blogger.com/video.g?token=AD6v5dzAvSGyZWwih0bzVPwsMFu0FHcicimjXdAy0gxcZ9wxp0eZgtTBuqScIg9zE-w4cLrNpPY9em4qvWfBZkyeFg' class='b-hbp-video b-uploaded' frameborder='0'></iframe></div>
<br />
<br />
Finally How do you record and trigger some actions if the test cases failed?<br />
<br />
Click on the "Test" link shown in below screenshot, and there are two fields to handle post test case and test suite exectuions either to record the runs or to trigger Power Automate flows or to send emails using power app code.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-PHABoHvAUoc/XkzuMj9QatI/AAAAAAAAHII/Q_jE_kyJ_m8QH99kOXGTsCUMsanKebcDACLcBGAsYHQ/s1600/12.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="572" data-original-width="1319" height="276" src="https://1.bp.blogspot.com/-PHABoHvAUoc/XkzuMj9QatI/AAAAAAAAHII/Q_jE_kyJ_m8QH99kOXGTsCUMsanKebcDACLcBGAsYHQ/s640/12.png" width="640" /></a></div>
<br />
<br />
Here is the link for MS documentation for Test Studio. <a href="https://powerapps.microsoft.com/en-us/blog/introducing-test-studio-to-build-end-to-end-tests-and-maintain-your-app-quality/">Link.</a><br />
<br />
Let me know if you have any questions or suggestions.<br />
<br />
<br />
<br /></div>
Pratap Reddy Pilakahttp://www.blogger.com/profile/17141976956878163883noreply@blogger.com0tag:blogger.com,1999:blog-4208378482021219922.post-59445340356781234732020-02-12T18:37:00.000-08:002020-02-13T14:07:08.374-08:00PowerApps - Beyond 500 item limit and Delegation Warning. The "XXXXX" part of this formula might not work on large datasets.<div dir="ltr" style="text-align: left;" trbidi="on">
This is the most common warning which is overlooked by the Power app developers as its just "Warning".<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-YXRJaMD_-bY/XkStJyiiwnI/AAAAAAAAHEU/JW6YmVOvSHw9ueXSAwoSsm6HD2z01bWzACLcBGAsYHQ/s1600/1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="86" data-original-width="576" height="94" src="https://1.bp.blogspot.com/-YXRJaMD_-bY/XkStJyiiwnI/AAAAAAAAHEU/JW6YmVOvSHw9ueXSAwoSsm6HD2z01bWzACLcBGAsYHQ/s640/1.png" width="640" /></a></div>
<br />
<br />
We see the function work and fetch the results, and thus ignore the warning. I have seen many of the powerapps that were productionized have these warnings.<br />
<br />
Please make a note:<br />
<br />
<h3 style="text-align: left;">
<span style="color: red;">"That is not just a technical warning, its a future functional error". </span></h3>
<br />
<a name='more'></a>In this post i will explain what does it mean and how to solve it.<br />
<br />
I have created a Sharepoint online site with a list of 6000+ user info.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/--v2syNmuiDk/XkSBMjxuilI/AAAAAAAAHDc/TiOfZQOQBKIwNlD7XjMgO9GJRjApIy-WQCLcBGAsYHQ/s1600/2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="558" data-original-width="801" height="443" src="https://1.bp.blogspot.com/--v2syNmuiDk/XkSBMjxuilI/AAAAAAAAHDc/TiOfZQOQBKIwNlD7XjMgO9GJRjApIy-WQCLcBGAsYHQ/s640/2.png" width="640" /></a></div>
<br />
<br />
I have also created a canvas power app where i am using the above list as a Data Source.<br />
<br />
Usually no application will try to show thousands of records and let users scroll down to record they want. So as a general requirement i am implemnting search box functionality here.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-_MxC5y8cgBw/XkSCj7bnGAI/AAAAAAAAHDo/m6jupbNRt2wSu1j8sEvs8lzQs2kPINmnwCLcBGAsYHQ/s1600/3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="579" data-original-width="1129" height="328" src="https://1.bp.blogspot.com/-_MxC5y8cgBw/XkSCj7bnGAI/AAAAAAAAHDo/m6jupbNRt2wSu1j8sEvs8lzQs2kPINmnwCLcBGAsYHQ/s640/3.png" width="640" /></a></div>
<br />
Now let me change the Data Source of the gallery from "PowerAppList" to a collection i created "RequiredData".<br />
<br />
And we will be populating the collection on click of the "Search" button. here is the code for that.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-F13uNCHkv48/XkSuJsDHWeI/AAAAAAAAHEk/J6adKMQIZYgMxqc_qweXivU6MygB0FzYQCLcBGAsYHQ/s1600/4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="172" data-original-width="893" height="122" src="https://1.bp.blogspot.com/-F13uNCHkv48/XkSuJsDHWeI/AAAAAAAAHEk/J6adKMQIZYgMxqc_qweXivU6MygB0FzYQCLcBGAsYHQ/s640/4.png" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
<b>Yeah, it shows a warning "Delegation Warning. The "Search" part of this fomrula might not work on large datasets.", but who cares, its working. let me show you the results.</b><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-NoyKADfzU9g/XkSEUWw4PVI/AAAAAAAAHD8/wrJ8UJpm85ogJjDLdws-uhG7z7MjRI0DwCLcBGAsYHQ/s1600/5.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="574" data-original-width="1106" height="332" src="https://1.bp.blogspot.com/-NoyKADfzU9g/XkSEUWw4PVI/AAAAAAAAHD8/wrJ8UJpm85ogJjDLdws-uhG7z7MjRI0DwCLcBGAsYHQ/s640/5.png" width="640" /></a></div>
<br />
See, it fetched many results from my sample data set and look ak that scroll bar, seems to be working. Its just a technical warning, no biggie, right? Now we productionize this.<br />
<br />
<b>Most of us didnt get to work with real dataset, which is quiet big compared to our sample dataset.</b><br />
<br />
To demonstrate this, i have created 6000+ records in that sharepoint list. So we have User1 to User6002. let me search for User50.<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-CDRc6wmM70g/XkSxJl_ZFLI/AAAAAAAAHEw/uUk3UhlYdxAYQh2pQqtnPjawTEtW_KVegCLcBGAsYHQ/s1600/6.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="523" data-original-width="1084" height="308" src="https://1.bp.blogspot.com/-CDRc6wmM70g/XkSxJl_ZFLI/AAAAAAAAHEw/uUk3UhlYdxAYQh2pQqtnPjawTEtW_KVegCLcBGAsYHQ/s640/6.png" width="640" /></a></div>
Oopsie ! There is the functional error i am talking about.<br />
<br />
When i search for users who have User50 in thier name, it should show, User50, User500,User501,User502... and so on. But here it cannot see data beyind User500 due to 500 Item limit in Powerapps.<br />
<br />
Before we discuss whats wrong, read below definition.<br />
<br />
<span style="font-size: large;">Delegation</span><br />
<span style="font-size: large;"><br /></span>
<b>Powerapps have a limit of 500 records per data source,</b> but there are ways of using large data sources with thousands of records.<br />
<h3 style="text-align: left;">
<i>Delegation is where the expressiveness of Power Apps formulas meets the need to minimize data moving over the network. In short, Power Apps will delegate the processing of data to the data source, rather than moving the data to the app for processing locally.</i></h3>
There are delegeable Data sources like SQL, Sharepoint, Power Automate and Logic Apps.<br />
But each of these data sources have certain commands that doesnt work for delegation.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-F13uNCHkv48/XkSuJsDHWeI/AAAAAAAAHEo/ZHy4_qoKsT8sbB6OUrtTKk2BFFZOT6aIwCEwYBhgL/s1600/4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="172" data-original-width="893" height="122" src="https://1.bp.blogspot.com/-F13uNCHkv48/XkSuJsDHWeI/AAAAAAAAHEo/ZHy4_qoKsT8sbB6OUrtTKk2BFFZOT6aIwCEwYBhgL/s640/4.png" width="640" /></a></div>
<br />
<br />
In my case, Search is not supported for Delegation of Sharepoint Data source.<br />
<br />
Please note that every function, every operator used in formula to query need to support Delegation, If i use Filter() with a Find() function it will not work as expected. Filter() supports delegation but Find() doesnot support delegation on Sharepoint list. let me demonstrate my point.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-9u7Ofvb_NPg/XkS1YPvGUYI/AAAAAAAAHFM/wzm0wTaSZCwzEz9vutETgqPoi7fehFBgwCLcBGAsYHQ/s1600/9.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="425" data-original-width="898" height="302" src="https://1.bp.blogspot.com/-9u7Ofvb_NPg/XkS1YPvGUYI/AAAAAAAAHFM/wzm0wTaSZCwzEz9vutETgqPoi7fehFBgwCLcBGAsYHQ/s640/9.png" width="640" /></a></div>
<br />
User60 search should fetch User60, User600,User601,User602... and so on.<br />
But due to the fact that the total formula is not delegeable, it looks at top 500 thus the above result.<br />
<br />
Now let me change the formula to make it delegeable. For Sharepoint both Filter() and StartsWith() functions supports Delegation. So it will work beyond 500 items now.<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-Kx0r2G6H0T0/XkSyaD6m2gI/AAAAAAAAHE8/doxZIlc16aQM3WtjYbEZBFoXODzRTSjvQCLcBGAsYHQ/s1600/7.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="169" data-original-width="910" height="118" src="https://1.bp.blogspot.com/-Kx0r2G6H0T0/XkSyaD6m2gI/AAAAAAAAHE8/doxZIlc16aQM3WtjYbEZBFoXODzRTSjvQCLcBGAsYHQ/s640/7.png" width="640" /></a></div>
<br />
Now I serach for User600 and here is the result.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-Aok-fk67lE4/XkSylZNzujI/AAAAAAAAHFA/YWr469WoPYUNE2yV6nERsA4tiy08cDLjACLcBGAsYHQ/s1600/8.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="610" data-original-width="1099" height="354" src="https://1.bp.blogspot.com/-Aok-fk67lE4/XkSylZNzujI/AAAAAAAAHFA/YWr469WoPYUNE2yV6nERsA4tiy08cDLjACLcBGAsYHQ/s640/8.png" width="640" /></a></div>
Please go through this MS article on what Data Sources supports what functions for delegation. Drop me a comment if you have any questions or queries.<br />
<a href="https://docs.microsoft.com/en-us/powerapps/maker/canvas-apps/delegation-overview#delegable-data-sources">https://docs.microsoft.com/en-us/powerapps/maker/canvas-apps/delegation-overview#delegable-data-sources</a><br />
<br />
As a best practice, alsways make the critical coumns on sharepoint list as a Indexed columns.<br />
<br /></div>
Pratap Reddy Pilakahttp://www.blogger.com/profile/17141976956878163883noreply@blogger.com0tag:blogger.com,1999:blog-4208378482021219922.post-7855910612631509422019-09-19T17:03:00.001-07:002019-09-19T17:03:13.598-07:00AWS Certification Pre-Exam Knowledge Refresh- Part 4<div dir="ltr" style="text-align: left;" trbidi="on">
<a href="https://pratapreddypilaka.blogspot.com/2019/09/aws-certification-pre-exam-knowledge_19.html" target="_blank">Here is the link for part 3</a><br />
<br />
<div lang="en-IE" style="font-family: Corbel; font-size: 14.0pt; margin-left: .375in; margin: 0in;">
<br /></div>
<div lang="en-IE" style="font-family: Corbel; font-size: 14.0pt; margin-left: .375in; margin: 0in;">
<span style="font-weight: bold;">EC2 Auto-Scaling</span> happens
Horizontally(Adding more instances) and<span style="font-weight: bold;"> DB
scaling</span> happens vertically(Adding more space on existing instance).</div>
<div lang="en-IE" style="font-family: Corbel; font-size: 14.0pt; margin-left: .375in; margin: 0in;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-QHhlaisUupY/XYQVmWmGfQI/AAAAAAAAG90/GGk6Ixc5ZZsSnZjUI9bkosVdUL79D1ghwCEwYBhgL/s1600/1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="900" data-original-width="1600" height="360" src="https://1.bp.blogspot.com/-QHhlaisUupY/XYQVmWmGfQI/AAAAAAAAG90/GGk6Ixc5ZZsSnZjUI9bkosVdUL79D1ghwCEwYBhgL/s640/1.png" width="640" /></a></div>
<div lang="en-IE" style="font-family: Corbel; font-size: 14.0pt; margin-left: .375in; margin: 0in;">
<span style="font-weight: bold;"></span></div>
<a name='more'></a>Auto Scale-in Algorithm:<br />
<div style="direction: ltr;">
<table border="1" cellpadding="0" cellspacing="0" style="border-collapse: collapse; border-color: #A3A3A3; border-style: solid; border-width: 1pt; direction: ltr; margin-left: .3333in;" summary="" title="" valign="top">
<tbody>
<tr>
<td style="border-color: #A3A3A3; border-style: solid; border-width: 1pt; padding: 4pt 4pt 4pt 4pt; vertical-align: top; width: 4.9277in;">
<div style="margin: 0in;">
</div>
<div lang="en-IE" style="font-family: Corbel; font-size: 14.0pt; margin: 0in;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-PpWTOxwIKtc/XYQVnxUJ7RI/AAAAAAAAG-E/a0TX5OP8yesrYHreS10Kmn32gPz93srXwCEwYBhgL/s1600/2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="589" data-original-width="457" height="400" src="https://1.bp.blogspot.com/-PpWTOxwIKtc/XYQVnxUJ7RI/AAAAAAAAG-E/a0TX5OP8yesrYHreS10Kmn32gPz93srXwCEwYBhgL/s400/2.png" width="310" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
</td>
<td style="border-color: #A3A3A3; border-style: solid; border-width: 1pt; padding: 4pt 4pt 4pt 4pt; vertical-align: top; width: 4.2291in;">
<div style="margin: 0in;">
</div>
<div lang="en-IE" style="font-family: Corbel; font-size: 14.0pt; margin: 0in;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-QTWFueVovbI/XYQVn5y1qvI/AAAAAAAAG-I/y6ubear6SOo6iGxUC90xUshVuBFzso1FACEwYBhgL/s1600/3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="545" data-original-width="491" height="320" src="https://1.bp.blogspot.com/-QTWFueVovbI/XYQVn5y1qvI/AAAAAAAAG-I/y6ubear6SOo6iGxUC90xUshVuBFzso1FACEwYBhgL/s320/3.png" width="288" /></a></div>
</td>
</tr>
</tbody></table>
</div>
<div lang="en-IE" style="font-family: Calibri; font-size: 11.0pt; margin-left: 6.375in; margin: 0in;">
<br /></div>
<div lang="en-IE" style="font-family: Calibri; font-size: 11.0pt; margin-left: .375in; margin: 0in;">
<br /></div>
<div lang="en-IE" style="font-family: Corbel; font-size: 14.0pt; margin-left: .375in; margin: 0in;">
<span style="font-weight: bold;">VPC </span></div>
<div lang="en-IE" style="font-family: Corbel; font-size: 14.0pt; margin-left: .375in; margin: 0in;">
<span style="font-weight: bold;">In a SubNet: </span>You loose 5 Ips</div>
<div lang="en-IE" style="font-family: Corbel; font-size: 14.0pt; margin-left: .375in; margin: 0in;">
.0 - Network</div>
<div lang="en-IE" style="font-family: Corbel; font-size: 14.0pt; margin-left: .375in; margin: 0in;">
.1 - Router</div>
<div lang="en-IE" style="font-family: Corbel; font-size: 14.0pt; margin-left: .375in; margin: 0in;">
.2 - DNS</div>
<div lang="en-IE" style="font-family: Corbel; font-size: 14.0pt; margin-left: .375in; margin: 0in;">
.3<span style="mso-spacerun: yes;"> </span>- Reserved</div>
<div lang="en-IE" style="font-family: Corbel; font-size: 14.0pt; margin-left: .375in; margin: 0in;">
.255 - Broadcast</div>
<div lang="en-IE" style="font-family: Corbel; font-size: 14.0pt; margin-left: .375in; margin: 0in;">
<br /></div>
<div lang="en-IE" style="font-family: Corbel; font-size: 14.0pt; margin-left: .375in; margin: 0in;">
<b>VGW - VPC Gateway to</b> communicate with your On-Prem DC</div>
<div lang="en-IE" style="font-family: Corbel; font-size: 14.0pt; margin-left: .375in; margin: 0in;">
Every subnet will have VPC CIDR with local - which allows the
components with in VPC to talk to each other.</div>
<div lang="en-IE" style="font-family: Corbel; font-size: 14.0pt; margin-left: .375in; margin: 0in;">
<b>Adding 0.0.0.0/0<span style="mso-spacerun: yes;"> </span>-
IGW(Internet Gateway) </b>will make the subnets public.</div>
<div lang="en-IE" style="font-family: Corbel; font-size: 14.0pt; margin-left: .375in; margin: 0in;">
On top of IGW on subnet, you still need to add Public IP for each
EC2. to make them public.</div>
<div lang="en-IE" style="font-family: Corbel; font-size: 14.0pt; margin-left: .375in; margin: 0in;">
<b>NAT gateway</b> is used by private VPC components to talk to internet.
But need to add 0.0.0.0/0 should be pointed to NATGW.</div>
<div lang="en-IE" style="font-family: Corbel; font-size: 14.0pt; margin-left: .375in; margin: 0in;">
<b>Network ACL</b> - Stateless allowing traffic In and out of subnet. For
connecting to a instance, you need to open both inbound and outbound
connections on NACL.</div>
<div lang="en-IE" style="font-family: Corbel; font-size: 14.0pt; margin-left: .375in; margin: 0in;">
<span style="font-weight: bold;">Security Group -</span> Stateful,
kind of firewall around EC2 Instances.</div>
<div lang="en-IE" style="font-family: Corbel; font-size: 14.0pt; margin-left: .375in; margin: 0in;">
<br /></div>
<div lang="en-IE" style="font-family: Corbel; font-size: 14.0pt; margin-left: .375in; margin: 0in;">
<span style="font-weight: bold;">Take note that an egress-only
Internet gateway is for use with IPv6 traffic only. To enable outbound-only
Internet communication over IPv4, use a NAT gateway instead.</span></div>
<div lang="en-IE" style="font-family: Corbel; font-size: 14.0pt; margin-left: .375in; margin: 0in;">
<br /></div>
<div lang="en-IE" style="font-family: Corbel; font-size: 14.0pt; margin-left: .375in; margin: 0in;">
<span style="font-weight: bold;">VPC Peering -</span> Connecting one
VPC to other VPCs. But other networks cannot access second VPC using Peering</div>
<div lang="en-IE" style="font-family: Corbel; font-size: 14.0pt; margin-left: .375in; margin: 0in;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-ZJeUlKZFOBc/XYQVoxbosKI/AAAAAAAAG-M/I5qL5fgQYMcABiDjIjfJ19NqTx7FK5tFACEwYBhgL/s1600/5.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="208" data-original-width="421" height="197" src="https://1.bp.blogspot.com/-ZJeUlKZFOBc/XYQVoxbosKI/AAAAAAAAG-M/I5qL5fgQYMcABiDjIjfJ19NqTx7FK5tFACEwYBhgL/s400/5.png" width="400" /></a></div>
<div lang="en-IE" style="font-family: Corbel; font-size: 14.0pt; margin-left: .375in; margin: 0in;">
<span style="font-weight: bold;">VPC Endpoints: </span>VPC Endpoints
are the way you can communicate to outside VPC on your private VPC network
without going through internet. There are 2 types on VPC Endpoints. </div>
<ol style="direction: ltr; font-family: Corbel; font-size: 14.0pt; font-style: normal; font-weight: normal; margin-bottom: 0in; margin-left: .75in; margin-top: 0in; unicode-bidi: embed;" type="1">
<li lang="en-IE" style="margin-bottom: 0; margin-top: 0; vertical-align: middle;" value="1"><span style="font-family: Corbel; font-family: Corbel; font-size: 14.0pt; font-size: 14.0pt; font-style: normal; font-weight: bold; font-weight: bold;">VPC
Interface Endpoints:</span><span style="font-family: Corbel; font-family: Corbel; font-size: 14.0pt; font-size: 14.0pt; font-style: normal; font-weight: normal;">
This is the access points setup on your VPC for other AWS services to
communicate. Eg: If a call need to be made by a SQS to components within
VPC, this will be used.</span></li>
<li lang="en-IE" style="margin-bottom: 0; margin-top: 0; vertical-align: middle;"><span style="font-family: Corbel; font-size: 14.0pt; font-weight: bold;">VPC Gateway
Endpoints</span><span style="font-family: Corbel; font-size: 14.0pt;">: This
is where your components within VPC need to interact with other AWS
services like S3 or a DynamoDB without going through internet. Remember
any gateway endpoints need to be listed in Route tables.</span></li>
</ol>
<div lang="en-IE" style="font-family: Corbel; font-size: 14.0pt; margin-left: .375in; margin: 0in;">
<br /></div>
<div lang="en-IE" style="font-family: Corbel; font-size: 14.0pt; margin-left: .375in; margin: 0in;">
<br /></div>
<div lang="en-IE" style="font-family: Corbel; font-size: 14.0pt; margin-left: .375in; margin: 0in;">
<span style="font-weight: bold;">IAM Policy</span> will have 3 parts :
Action, Effect, Resource</div>
<div lang="en-IE" style="font-family: Corbel; font-size: 14.0pt; margin-left: .375in; margin: 0in;">
AWS Organizations uses <span style="font-weight: bold;">SCP (service
control policies)</span> for enforcing restrictions on almost all the users
including Root.</div>
<div lang="en-IE" style="font-family: Corbel; font-size: 14.0pt; margin-left: .375in; margin: 0in;">
<br /></div>
<div lang="en-IE" style="font-family: Corbel; font-size: 14.0pt; margin-left: .375in; margin: 0in;">
<span style="font-weight: bold;">Instance metadata</span> is the data
about your instance that you can use to configure or manage the running
instance. You can get the instance ID, public keys, public IP address and many
other information from the instance metadata by firing a URL command in your
instance to this URL:</div>
<div lang="en-IE" style="font-family: Corbel; font-size: 14.0pt; margin-left: .375in; margin: 0in;">
<a href="http://169.254.169.254/latest/meta-data/">http://169.254.169.254/latest/meta-data/</a></div>
<div lang="en-IE" style="font-family: Corbel; font-size: 14.0pt; margin-left: .375in; margin: 0in;">
<br /></div>
<div lang="en-IE" style="font-family: Corbel; font-size: 14.0pt; margin-left: .375in; margin: 0in;">
<span style="font-weight: bold;">AWS Security Token Service (AWS STS)</span>
is the service that you can use to create and provide trusted users with
temporary security credentials that can control access to your AWS resources.</div>
<div lang="en-IE" style="font-family: Corbel; font-size: 14.0pt; margin-left: .375in; margin: 0in;">
<br /></div>
<div lang="en-IE" style="font-family: Corbel; font-size: 14.0pt; margin-left: .375in; margin: 0in;">
<span style="font-weight: bold;">AWS DR Strategies:</span></div>
<div lang="en-IE" style="font-family: Corbel; font-size: 14.0pt; margin-left: .375in; margin: 0in;">
<span style="font-weight: bold;"><span style="mso-spacerun: yes;">
</span>Economical--------------------------------------------------------Costly</span></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-zcdzc3-fW94/XYQVo73oihI/AAAAAAAAG-Q/uX1J07JDlfQZYin-5hsQeCSv7-t6VyWGwCEwYBhgL/s1600/6.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="120" data-original-width="570" height="134" src="https://1.bp.blogspot.com/-zcdzc3-fW94/XYQVo73oihI/AAAAAAAAG-Q/uX1J07JDlfQZYin-5hsQeCSv7-t6VyWGwCEwYBhgL/s640/6.png" width="640" /></a></div>
<div lang="en-IE" style="font-family: Corbel; font-size: 14.0pt; margin-left: .375in; margin: 0in;">
<span style="font-weight: bold;"><br /></span></div>
<div lang="en-IE" style="font-family: Corbel; font-size: 14.0pt; margin-left: .375in; margin: 0in;">
<span style="font-weight: bold;"><br /></span></div>
<div lang="en-IE" style="font-family: Corbel; font-size: 14.0pt; margin-left: .375in; margin: 0in;">
<span style="font-weight: bold;">Pilot Light</span> is a DR scenario
in which a minimal version of an environment is always running in the cloud.</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-Sogjy7ShaT8/XYQVo3ESG9I/AAAAAAAAG-U/RIWy3Kz8zusFsXlz5HB38NHSVo9PPXPMQCEwYBhgL/s1600/7.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="354" data-original-width="556" height="406" src="https://1.bp.blogspot.com/-Sogjy7ShaT8/XYQVo3ESG9I/AAAAAAAAG-U/RIWy3Kz8zusFsXlz5HB38NHSVo9PPXPMQCEwYBhgL/s640/7.png" width="640" /></a></div>
<div lang="en-IE" style="font-family: Corbel; font-size: 14.0pt; margin-left: .375in; margin: 0in;">
<br /></div>
<div lang="en-IE" style="font-family: Corbel; font-size: 14.0pt; margin-left: .375in; margin: 0in;">
<br /></div>
<div lang="en-IE" style="font-family: Corbel; font-size: 14.0pt; margin-left: .375in; margin: 0in;">
<span style="font-weight: bold;">AWS Cognito </span>works on 2
components. USER Pool and Identity Pool. You can enable MFA on User Pools.</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-5SkPZor8vuE/XYQVpiGt-AI/AAAAAAAAG-Y/S55g8FKQc2ospjIe8Mt4JkXPDGr7e5uXQCEwYBhgL/s1600/8.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="412" data-original-width="560" height="468" src="https://1.bp.blogspot.com/-5SkPZor8vuE/XYQVpiGt-AI/AAAAAAAAG-Y/S55g8FKQc2ospjIe8Mt4JkXPDGr7e5uXQCEwYBhgL/s640/8.png" width="640" /></a></div>
<div lang="en-IE" style="font-family: Corbel; font-size: 14.0pt; margin-left: .375in; margin: 0in;">
<br /></div>
<div lang="en-IE" style="font-family: Corbel; font-size: 14.0pt; margin-left: .375in; margin: 0in;">
<br /></div>
<div lang="en-IE" style="font-family: Corbel; font-size: 14.0pt; margin-left: .375in; margin: 0in;">
<span style="font-weight: bold;">AWS Cognito Works like this:</span></div>
<ul style="direction: ltr; margin-bottom: 0in; margin-left: .75in; margin-top: 0in; unicode-bidi: embed;" type="disc">
<li lang="en-IE" style="margin-bottom: 0; margin-top: 0; vertical-align: middle;"><span style="font-family: Corbel; font-size: 14.0pt;">User authenticate with FB,
Google or other auth providers.</span></li>
<li lang="en-IE" style="margin-bottom: 0; margin-top: 0; vertical-align: middle;"><span style="font-family: Corbel; font-size: 14.0pt;">Get the Authenticated UserID</span></li>
<li lang="en-IE" style="margin-bottom: 0; margin-top: 0; vertical-align: middle;"><span style="font-family: Corbel; font-size: 14.0pt;">Then send it to AWS Cognito
and get Cognito OpenID Token (CognitoID)</span></li>
<li lang="en-IE" style="margin-bottom: 0; margin-top: 0; vertical-align: middle;"><span style="font-family: Corbel; font-size: 14.0pt;">Send it to AWS STS to assume a
role or get Temporary access via Access Keys</span></li>
</ul>
<div lang="en-IE" style="font-family: Corbel; font-size: 14.0pt; margin-left: .375in; margin: 0in;">
<a href="https://1.bp.blogspot.com/-3IRQJK6rOxo/XYQVqO9qhTI/AAAAAAAAG-c/6D3SPNq_GL8pzmw3SnCi_O442L0IoZqEACEwYBhgL/s1600/9.png" imageanchor="1" style="font-size: 14pt; margin-left: 1em; margin-right: 1em; text-align: center;"><img border="0" data-original-height="461" data-original-width="720" height="408" src="https://1.bp.blogspot.com/-3IRQJK6rOxo/XYQVqO9qhTI/AAAAAAAAG-c/6D3SPNq_GL8pzmw3SnCi_O442L0IoZqEACEwYBhgL/s640/9.png" width="640" /></a></div>
<div lang="en-IE" style="font-family: Corbel; font-size: 14.0pt; margin-left: .375in; margin: 0in;">
<span style="font-weight: bold;">Amazon Kinesis Data Firehose</span>
is the easiest way to load streaming data into data stores and analytics tools.
It can capture, transform, and load streaming data into Amazon S3, Amazon
Redshift, Amazon Elasticsearch Service, and Splunk, enabling near real-time
analytics with existing business intelligence tools and dashboards you are
already using today.</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-taiCg_PNuTU/XYQVmSQN-vI/AAAAAAAAG94/flq3EejfP3Mss7VHi3f5ZfH4GhXq7uTBQCEwYBhgL/s1600/10.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="302" data-original-width="796" height="242" src="https://1.bp.blogspot.com/-taiCg_PNuTU/XYQVmSQN-vI/AAAAAAAAG94/flq3EejfP3Mss7VHi3f5ZfH4GhXq7uTBQCEwYBhgL/s640/10.png" width="640" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-BLAAmYIt_0Y/XYQVmTits1I/AAAAAAAAG98/eEtlAXNV3rID7Rv2sWgqTScUtKPatkq5ACEwYBhgL/s1600/11.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="217" data-original-width="657" height="210" src="https://1.bp.blogspot.com/-BLAAmYIt_0Y/XYQVmTits1I/AAAAAAAAG98/eEtlAXNV3rID7Rv2sWgqTScUtKPatkq5ACEwYBhgL/s640/11.gif" width="640" /></a></div>
<div lang="en-IE" style="font-family: Corbel; font-size: 14.0pt; margin-left: .375in; margin: 0in;">
<br /></div>
<div lang="en-IE" style="font-family: Corbel; font-size: 14.0pt; margin-left: .375in; margin: 0in;">
<br /></div>
<div lang="en-IE" style="font-family: Corbel; font-size: 14.0pt; margin-left: .375in; margin: 0in;">
<span style="font-weight: bold;">Amazon Kinesis Data Streams:</span>
By defaults retention period is 24 hours and can be extended to 168 hours.</div>
<div lang="en-IE" style="color: #444444; font-family: Corbel; font-size: 14.0pt; margin-left: .375in; margin: 0in;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-Uz7Tn9nu6AY/XYQVnn8FjlI/AAAAAAAAG-A/DVxdgW4Gr7EdLmcQm0RQJUH-44PEJTWmQCEwYBhgL/s1600/12.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="642" data-original-width="1425" height="288" src="https://1.bp.blogspot.com/-Uz7Tn9nu6AY/XYQVnn8FjlI/AAAAAAAAG-A/DVxdgW4Gr7EdLmcQm0RQJUH-44PEJTWmQCEwYBhgL/s640/12.png" width="640" /></a></div>
<div lang="en-IE" style="color: #444444; font-family: Corbel; font-size: 14.0pt; margin-left: .375in; margin: 0in;">
<br /></div>
<div style="color: #444444; font-family: Corbel; font-size: 14.0pt; margin-left: .375in; margin: 0in;">
<span style="background: white; font-weight: bold;">Amazon SWF</span><span style="background: white;"> interacts with activity workers and deciders by
providing them with work assignments known as tasks. There are three types of
tasks in Amazon SWF:</span></div>
<ul style="direction: ltr; margin-bottom: 0in; margin-left: .75in; margin-top: 0in; unicode-bidi: embed;" type="disc">
<li style="color: #444444; margin-bottom: 0; margin-top: 0; vertical-align: middle;"><span lang="en-NZ" style="font-family: Corbel; font-size: 14.0pt; font-weight: bold;">Activity
task</span><span lang="en-IE" style="font-family: Corbel; font-size: 14.0pt;"> </span><span lang="en-NZ" style="font-family: Corbel; font-size: 14.0pt;">– An</span><span lang="en-IE" style="font-family: Corbel; font-size: 14.0pt;"> </span><span lang="en-IE" style="font-family: Corbel; font-size: 14.0pt; font-style: italic;">Activity</span><span lang="en-IE" style="font-family: Corbel; font-size: 14.0pt;"> </span><span lang="en-NZ" style="font-family: Corbel; font-size: 14.0pt;">task tells an
activity worker to perform its function, such as to check inventory or
charge a credit card. The activity task contains all the information that
the activity worker needs to perform its function.</span></li>
<li style="color: #444444; margin-bottom: 0; margin-top: 0; vertical-align: middle;"><span lang="en-NZ" style="color: #444444; font-family: Corbel; font-size: 14.0pt; font-weight: bold;">Lambda task</span><span lang="en-IE" style="color: #444444; font-family: Corbel; font-size: 14.0pt;"> </span><span lang="en-NZ" style="color: #444444; font-family: Corbel; font-size: 14.0pt;">– A</span><span lang="en-IE" style="color: #444444; font-family: Corbel; font-size: 14.0pt;"> </span><span lang="en-IE" style="color: #444444; font-family: Corbel; font-size: 14.0pt; font-style: italic;">Lambda</span><span lang="en-IE" style="color: #444444; font-family: Corbel; font-size: 14.0pt;"> </span><span lang="en-NZ" style="color: #444444; font-family: Corbel; font-size: 14.0pt;">task is similar to an Activity
task, but executes a Lambda function instead of a traditional Amazon SWF
activity. For more information about how to define a Lambda task, see</span><span lang="en-IE" style="color: #444444; font-family: Corbel; font-size: 14.0pt;"> </span><a href="https://docs.aws.amazon.com/amazonswf/latest/developerguide/lambda-task.html"><span lang="en-IE" style="font-family: Corbel; font-size: 14.0pt;">AWS hf Tasks</span></a><span lang="en-NZ" style="color: #444444; font-family: Corbel; font-size: 14.0pt;">.</span></li>
<li style="color: #444444; margin-bottom: 0; margin-top: 0; vertical-align: middle;"><span lang="en-NZ" style="font-family: Corbel; font-size: 14.0pt; font-weight: bold;">Decision
task</span><span lang="en-IE" style="font-family: Corbel; font-size: 14.0pt;"> </span><span lang="en-NZ" style="font-family: Corbel; font-size: 14.0pt;">– A</span><span lang="en-IE" style="font-family: Corbel; font-size: 14.0pt;"> </span><span lang="en-IE" style="font-family: Corbel; font-size: 14.0pt; font-style: italic;">Decision</span><span lang="en-IE" style="font-family: Corbel; font-size: 14.0pt;"> </span><span lang="en-NZ" style="font-family: Corbel; font-size: 14.0pt;">task tells a
decider that the state of the workflow execution has changed so that the
decider can determine the next activity that needs to be performed. The
decision task contains the current workflow history.</span></li>
</ul>
<div lang="en-IE" style="color: #444444; font-family: Corbel; font-size: 14.0pt; margin-left: .375in; margin: 0in;">
<br /></div>
<div style="margin-left: .375in; margin: 0in;">
<span lang="en-IE" style="color: #444444; font-family: Corbel; font-size: 14.0pt; font-weight: bold;">Routing Policies
of Route53</span><span lang="en-IE" style="color: #444444; font-family: Corbel; font-size: 14.0pt;">: </span><a href="https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/routing-policy.html"><span lang="en-NZ" style="font-family: Calibri; font-size: 11.0pt;">https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/routing-policy.html</span></a></div>
<div lang="en-IE" style="color: #444444; font-family: Corbel; font-size: 14.0pt; margin-left: .375in; margin: 0in;">
<br /></div>
<div style="color: #444444; font-family: Corbel; font-size: 14.0pt; margin-left: .375in; margin: 0in;">
<span style="font-weight: bold;">Amazon Route 53 currently
supports the following DNS record types:</span></div>
<ul style="direction: ltr; margin-bottom: 0in; margin-left: .375in; margin-top: 0in; unicode-bidi: embed;" type="disc">
<li style="color: #444444; margin-bottom: 0; margin-top: 0; vertical-align: middle;"><span style="font-family: Corbel; font-size: 14.0pt;">-A (address record)</span></li>
<li style="color: #444444; margin-bottom: 0; margin-top: 0; vertical-align: middle;"><span style="font-family: Corbel; font-size: 14.0pt;">-AAAA (IPv6 address record)</span></li>
<li style="color: #444444; margin-bottom: 0; margin-top: 0; vertical-align: middle;"><span style="font-family: Corbel; font-size: 14.0pt;">-CNAME (canonical name record)</span></li>
<li style="color: #444444; margin-bottom: 0; margin-top: 0; vertical-align: middle;"><span style="font-family: Corbel; font-size: 14.0pt;">-CAA (certification authority
authorization)</span></li>
<li style="color: #444444; margin-bottom: 0; margin-top: 0; vertical-align: middle;"><span style="font-family: Corbel; font-size: 14.0pt;">-MX (mail exchange record)</span></li>
<li style="color: #444444; margin-bottom: 0; margin-top: 0; vertical-align: middle;"><span style="font-family: Corbel; font-size: 14.0pt;">-NAPTR (name authority pointer
record)</span></li>
<li style="color: #444444; margin-bottom: 0; margin-top: 0; vertical-align: middle;"><span style="font-family: Corbel; font-size: 14.0pt;">-NS (name server record)</span></li>
<li style="color: #444444; margin-bottom: 0; margin-top: 0; vertical-align: middle;"><span style="font-family: Corbel; font-size: 14.0pt;">-PTR (pointer record)</span></li>
<li style="color: #444444; margin-bottom: 0; margin-top: 0; vertical-align: middle;"><span style="font-family: Corbel; font-size: 14.0pt;">-SOA (start of authority
record)</span></li>
<li style="color: #444444; margin-bottom: 0; margin-top: 0; vertical-align: middle;"><span style="font-family: Corbel; font-size: 14.0pt;">-SPF (sender policy framework)</span></li>
<li style="color: #444444; margin-bottom: 0; margin-top: 0; vertical-align: middle;"><span style="font-family: Corbel; font-size: 14.0pt;">-SRV (service locator)</span></li>
<li style="color: #444444; margin-bottom: 0; margin-top: 0; vertical-align: middle;"><span style="font-family: Corbel; font-size: 14.0pt;">-TXT (text record)</span></li>
</ul>
<div lang="en-IE" style="color: #444444; font-family: Corbel; font-size: 14.0pt; margin-left: .375in; margin: 0in;">
<br /></div>
<div lang="en-IE" style="color: #444444; font-family: Corbel; font-size: 14.0pt; margin-left: .375in; margin: 0in;">
<span style="font-weight: bold;">Connection Draining</span>
: To ensure that a Classic Load Balancer stops sending requests to instances
that are de-registering or unhealthy while keeping the existing connections
open, use connection draining</div>
<br /><br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
</div>
Pratap Reddy Pilakahttp://www.blogger.com/profile/17141976956878163883noreply@blogger.com0