<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: MertSenel</title>
    <description>The latest articles on DEV Community by MertSenel (@mertsenel).</description>
    <link>https://dev.to/mertsenel</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F258266%2Ffd39f0b9-2ba2-4cf4-8389-ddebc2c72022.jpeg</url>
      <title>DEV Community: MertSenel</title>
      <link>https://dev.to/mertsenel</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mertsenel"/>
    <language>en</language>
    <item>
      <title>Why Allowing Engineers to 'Fail Safely' Is Crucial for High-Quality Software Development</title>
      <dc:creator>MertSenel</dc:creator>
      <pubDate>Sun, 22 Oct 2023 10:48:06 +0000</pubDate>
      <link>https://dev.to/mertsenel/why-allowing-engineers-to-fail-safely-is-crucial-for-high-quality-software-development-4952</link>
      <guid>https://dev.to/mertsenel/why-allowing-engineers-to-fail-safely-is-crucial-for-high-quality-software-development-4952</guid>
      <description>&lt;p&gt;In the ever-evolving landscape of software development, there's been increasing chatter about how the process of building, testing, and deploying software has ostensibly become easier. With a plethora of new toolsets available to software engineers and adjacent tech professionals, like quality engineers, cybersecurity experts, and DevOps engineers, one might believe the journey has become smoother. However, this perspective tends to oversimplify the inherent complexities.&lt;/p&gt;

&lt;p&gt;Yes, automation, generative AI, and specialized tools have streamlined many mundane tasks. Yet, the expectations from engineers have simultaneously risen, particularly in the domain of business context. Modern engineers are more entwined with their products and services than ever before, directly influencing the financial bottom lines of businesses, partners, and customers. Those who excel in this environment are not just technically proficient but also possess a keen understanding of business vision, strategy, and principles. Such a holistic perspective aids in decision-making, especially in roles that require frequent determinations.&lt;/p&gt;

&lt;p&gt;However, as distributed systems expand and integration points multiply, the cognitive load on engineers intensifies. It's crucial, now more than ever, for engineers to operate within a well-structured and efficient environment, one that aids in managing this cognitive overload and ensures optimal outcomes.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Philosophy of "Failing Safely"
&lt;/h2&gt;

&lt;p&gt;A term that's gained traction in the tech community is the concept of "failing safely." I recently attended a software testing conference, and one statement deeply resonated with me: "There's no 100% bug-free software or system. Striving for perfection would only allow our competitors to outpace us in the market.This sentiment underscores the inherent risks every time we ship software.&lt;/p&gt;

&lt;p&gt;But how can we mitigate these risks? The answer lies not just in the post-development phase but primarily in the pre-deployment testing phase. This principle holds for both software developers and the architects behind the infrastructure of these intricate distributed systems.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Components of a Safe Development Environment
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---JtYKb_4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/06wmp6kwe3nffov7dcmf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---JtYKb_4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/06wmp6kwe3nffov7dcmf.png" alt='"Local Testing Capabilities"' width="800" height="457"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Local Testing Capabilities:&lt;/strong&gt; The foundation of any development process is the ability to perform local testing. Engineers should have the tools and environments that mirror real-world scenarios as closely as possible. By simulating external integrations, databases, and other dependencies locally, developers can identify and fix issues before they reach a shared environment. Utilizing containerization technologies can further standardize this process, ensuring that all team members work within consistent parameters, reducing the "it works on my machine" phenomenon.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--B9rUxKJZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/11ugexjw5xhca452ppe9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--B9rUxKJZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/11ugexjw5xhca452ppe9.png" alt='"Disposable Environments"' width="800" height="457"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Disposable Environments:&lt;/strong&gt; The cloud age has brought about the possibility of spinning up environments on-demand and tearing them down when no longer needed. These temporary environments are invaluable for testing complex features, especially in distributed systems where interactions between components can be unpredictable. By defining infrastructure as code, these environments can be version-controlled, ensuring repeatability and consistency across various stages of development.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zBWGK4LN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a0p33aul9dd7k5e25w3e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zBWGK4LN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a0p33aul9dd7k5e25w3e.png" alt='docs/sandbox_or_playground_areas.png" title=' width="800" height="457"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sandbox or Playground Areas:&lt;/strong&gt; Offering engineers a safe, isolated space to experiment and learn is essential for innovation. These sandboxes, separate from production and even development environments, allow for risk-taking without the fear of negative consequences. Such areas are not just for testing new features but also for training, upskilling, and trying out new technologies or methodologies.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5Zjt5ELX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rb5hwsorpjgrn801ik7b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5Zjt5ELX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rb5hwsorpjgrn801ik7b.png" alt='"Automated Rollbacks"' width="800" height="457"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Automated Rollbacks:&lt;/strong&gt; In the fast-paced world of continuous deployment, the ability to quickly revert changes is paramount. Automated rollback mechanisms ensure that if a newly deployed feature causes issues, systems can instantly return to a previously stable state. This reduces downtime and user impact, ensuring that services remain reliable even when deployments don't go as planned.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--K7rB0kgO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2assq6q94qp0qonfu6jz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--K7rB0kgO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2assq6q94qp0qonfu6jz.png" alt='"Monitoring and Observability"' width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Monitoring and Observability:&lt;/strong&gt; Beyond traditional monitoring, observability provides insights into the internal state of systems based on external outputs. It's not just about knowing when something goes wrong, but understanding why. By integrating comprehensive observability tools, engineers can get a granular view of system behavior, facilitating quicker debugging and more informed decision-making.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--C6j1c-Lq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mxm9cqw7z6spkkl5r21a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--C6j1c-Lq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mxm9cqw7z6spkkl5r21a.png" alt='"Blameless Post-Mortems"' width="800" height="457"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Blameless Post-Mortems:&lt;/strong&gt; Mistakes are inevitable. What matters is how teams respond. Cultivating a blame-free culture where the focus is on learning and improving, rather than pointing fingers, is essential. Blameless post-mortems encourage open discussions about what went wrong and how to prevent similar issues in the future, fostering a culture of continuous improvement.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--amSm2pu---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0xlji2l9oedl9pynagfe.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--amSm2pu---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0xlji2l9oedl9pynagfe.png" alt='"Feature Flags"' width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Feature Flags:&lt;/strong&gt; These are powerful tools that allow developers to release new functionalities in a controlled manner. By toggling features on or off, they can be tested in live environments with a subset of users, gathering feedback and ensuring stability before a full-scale rollout. This granular control reduces risks associated with deployments and allows for more iterative development.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Uvv82W87--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2o4obusp6c39gsajve1j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Uvv82W87--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2o4obusp6c39gsajve1j.png" alt='"Pair Programming"' width="800" height="457"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Encourage Pair Programming:&lt;/strong&gt; Two heads are often better than one. Pair programming promotes knowledge sharing, reduces coding errors, and fosters a collaborative team culture. By having two developers work on a piece of code together, they can brainstorm solutions, review each other's code in real-time, and ensure that the best possible solution is achieved.&lt;/p&gt;

&lt;p&gt;In conclusion, creating a nurturing development environment is about more than just tools and processes; it's about fostering a culture where engineers feel empowered to innovate, learn from mistakes, and consistently deliver high-quality software. By focusing on these key components, organizations set the stage for excellence, driving growth, and ensuring long-term success.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>softwareengineering</category>
      <category>testing</category>
      <category>culture</category>
    </item>
    <item>
      <title>Stages of Platform Engineering and State of DevOps</title>
      <dc:creator>MertSenel</dc:creator>
      <pubDate>Wed, 18 Jan 2023 11:19:07 +0000</pubDate>
      <link>https://dev.to/mertsenel/stages-of-platform-engineering-and-state-of-devops-35d2</link>
      <guid>https://dev.to/mertsenel/stages-of-platform-engineering-and-state-of-devops-35d2</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--yWSNUYI4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xcxkmpg8davzmpzxe75k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--yWSNUYI4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xcxkmpg8davzmpzxe75k.png" alt="Image description" width="880" height="528"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Platform Engineering and Different Stages of DevOps Maturity
&lt;/h2&gt;

&lt;p&gt;Platform Engineering is term you might have heard of but not yet got to know what it exactly is. Platform's are a set of infrastructure that takes care of compute, persistent storage, networking and utilities such as monitoring, alerting and observability. This is a convenient way of boosting velocity in going to market for a service that is being developed. One of the popular recipe is having a compute platform at core such as Kubernetes and complementing it with cloud PaaS offering to provide things such as, messaging, storage, database or caching or similar infrastructure. If you also have pre modelled designs for high availability, geographical redundancy, security you should be having a streamlined process for launching services to production in small amount of time. &lt;/p&gt;

&lt;p&gt;Today I wish to talk about some maturity levels in terms of DevOps automation that makes up your capability to onboard and launch services on your Platform. You might have a great platform but often times the tooling and automation behind your platform defines to ease up adoption for new services and developers using your platform. Is your platform team is required to aid developers and squads to onboard themselves and their services on your platform or are you in a state to offer "paved roads" to them that they can easily make use of and be happy with the end result. As most things in DevOps the answer is not binary and you can assess and see which stage you are at, right now. Where are you today, also should define the next milestone you should aim for, and finally achieve a cutting edge platform capability for your team and your stake holders. Without further due lets get into the different stages of maturity and what they exactly means. &lt;/p&gt;

&lt;h3&gt;
  
  
  Stage 1: Lack of Capability
&lt;/h3&gt;

&lt;p&gt;This is a stage where your Platform team doesn't know how to solve a problem. For example a delivery squad may come to you with requirement such as, they want to have a containerized application, with need of a NoSQL database and a Redis cache. Your team at this moment, don't have a reference on how to continuously integrate this solution, how to consistently, build, lint, test, security scan, package and distribute such software. Then there is the problem of having a highly available, redundant and secure infrastructure that you can monitor, alert and observe that would allow you to operate this piece of software in a production environment. &lt;/p&gt;

&lt;p&gt;This state is your starting point and you may be at this stage, this is the point where you may decide on some long term solutions. If you expect a lot more of similar problems down the line, you may decide on building a platform so you can have reusable solution to these requirements for workloads of same or similar nature.&lt;/p&gt;

&lt;h3&gt;
  
  
  Stage 2: Custom Solutions
&lt;/h3&gt;

&lt;p&gt;You have now started building a solution based on the request you have received. You started developing infrastructure as code, pipeline as code, configuration as code to package and deploy the service. You may parametrize the artifacts you build to be able to accommodate different Software development lifecycle environments such as development, qa, uat, staging, production with different levels of SKUs and redundancy levels. However at this stage whatever you built is most likely bespoke and it would still be labor intensive to scale this same solution for other services. &lt;/p&gt;

&lt;p&gt;The best solution you have at this stage is to duplicate the parametrized artifacts and alter them to fit for the next service you need to onboard and host on your platform, but it still requires a good amount of effort and it needs your platform team's direct involvement to enable the delivery squad to develop and launch their service. &lt;/p&gt;

&lt;h3&gt;
  
  
  Stage 3: Reusable &amp;amp; Central Artifacts
&lt;/h3&gt;

&lt;p&gt;At this stage, you have moved all of your parametrized artifacts of pipeline, infrastructure as code, configuration as code monitoring artifacts into centralized repositories. The reusable artifacts are developed in a manner that they are highly parametrized and considered as generic modules that can accommodate a wide range of scenarios. &lt;/p&gt;

&lt;p&gt;As a platform team you provide and maintain reference architectures that showcases a recommended way of consuming the generic modules you have developed and this provides a streamlined way of onboarding and developing a service that would by design be compliant and secure that has the serviceability and supportability requirements fulfilled from get go. This is called a "paved road", it would be the road that'd be taking has benefits in terms of being the road to production with least of resistance. It'd be encouraged and desired to take by everyone as it'd have better support from the platform team and provide convenience to developers while developing new services. &lt;/p&gt;

&lt;h3&gt;
  
  
  Stage 4: Automated Bootstrapping
&lt;/h3&gt;

&lt;p&gt;As patterns emerges, the platform team should be able to spot certain use cases on how their platform it getting used. They may even define some baseline resources, common denominators they'd consider as a bare minimum requirements for a service. At this stage platform team would create some high level automation that leverages the distributed automation workflows they have already built in the previous stages to create high level automation workflows that'd make the onboarding and progression to production much easier and streamlined for new services. &lt;/p&gt;

&lt;p&gt;This top level automation should can be more opinionated and should able to configured by small set of parameters such as project keyword, environments needed and small set of selections to be made by the consumer. At this stage the consumption of the automation is so easy that the consumer can be both the platform team that provides and maintains this automation workflow or the consumer can be directly the delivery squad that would be developing the service they wish to onboard and launch to production on the platform. This stage provides better velocity, developer experience as the involvement required from the platform specialist is lowered.&lt;/p&gt;

&lt;h3&gt;
  
  
  Stage 5: Environment as a Service
&lt;/h3&gt;

&lt;p&gt;At this stage the platform team incorporates a platform orchestration tool to abstract the automation one more level and provide a way to orchestrate even more automation in one single flow. By using specialized tools, they allow to provision and maintain environments with minimal effort as orchestrator acts as a top level actor that initiates and governs the environments generated by this tool.&lt;/p&gt;

&lt;p&gt;The environments generated, although should be fit for purpose for majority of the cases, the platform team would still be in consultant capacity to provide the "last mile engineering" to fill in the gaps and make the solutions provided satisfying all of the requirements of services if they need this assistance. &lt;/p&gt;

&lt;p&gt;The environments generated at this stage are all adhering to "paved road" principles that are secure, compliant and reliable by design. The platform orchestrator provides a process to provision and manage the environments created with the Day 2 activities such as maintaining and updating in a high scale.&lt;/p&gt;

&lt;h3&gt;
  
  
  Stage 6: Internal Developer Portal
&lt;/h3&gt;

&lt;p&gt;Internal developer portals, are internally available web applications that provides a self-service portal for developers to consume the "paved roads" developed by the platform team. At this stage as the consumption and governance shifts towards the delivery squads the platform and it's offering also makes a shift in maturity and model. &lt;/p&gt;

&lt;p&gt;At this stage, the platform and it's features are considered as a product. The platform team being the custodians of the platform would still be required to educate delivery squads and provide consultancy to delivery squads to consume their offerings but their main duty is to maintain and update their platform, as a product. &lt;/p&gt;

&lt;p&gt;Internal Developer Portal, acts as an "App Store" for infrastructure and supporting tooling for a service. The consumers are not only be able to provision resources required for their services but also be able to view the things that they own, manage and perform administrative tasks on them via using the developer portal. This stage is considered as the pinnacle of the automation &amp;amp; DevOps maturity that you can have for your platform and it can have an excellence level as much as you are willing to put efforts into it. Just reaching this stage as a minimum viable product is a pretty significant achievement, but from here onwards sky would be the limit. &lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In DevOps, the iterative value delivery is one of the key factors. The important thing is to have a direction to follow and align your efforts to reach the state you are aiming to reach. I have tried to give you an overview of different maturity levels for DevOps and automation capabilities, so you can asses what your organisations current level is and what target state you can aim, for your next sprint of efforts. I hope this you find this article, helpful in understanding your current state and strategising your ideal state in the upcoming future. &lt;/p&gt;

</description>
      <category>devops</category>
      <category>strategy</category>
      <category>platform</category>
    </item>
    <item>
      <title>SSL Certificate Expiry Checker</title>
      <dc:creator>MertSenel</dc:creator>
      <pubDate>Sat, 17 Dec 2022 16:45:14 +0000</pubDate>
      <link>https://dev.to/mertsenel/ssl-certificate-expiry-checker-7jc</link>
      <guid>https://dev.to/mertsenel/ssl-certificate-expiry-checker-7jc</guid>
      <description>&lt;p&gt;&lt;strong&gt;Track Your SSL Certificate Expiry Dates via CI/CD Pipelines&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--TjPQ_4fW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0wsk71v11uand2q6tt8h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--TjPQ_4fW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0wsk71v11uand2q6tt8h.png" alt="SSLCertificateExpiryTracker" width="880" height="653"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Custom HTML Report Sample&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--fdFZoPIe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mabax38h12p55ve6ed2x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fdFZoPIe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mabax38h12p55ve6ed2x.png" alt="CustomHTMLReport_Sample" width="880" height="186"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Code Repository: &lt;a href="https://github.com/MertSenel/ssl-certificate-expiry-checker"&gt;https://github.com/MertSenel/ssl-certificate-expiry-checker&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;Operation teams are responsible for rotating SSL certificates of the services they are responsible for which have HTTPS endpoints.&lt;br&gt;
Although it's better to automate the full rotation process, this may not always possible. Regardless of the rotation process a regular check can help with keeping on top of overall SSL certificate health, expiry being the most important metric. We also know that, as of 2020 most certificate authorities limit their maximum validity period for newly issues certificates to 1 year. This adds a big burden to teams which are responsible for multitude of domain names and SSL certificates issued for relevant subject names.&lt;/p&gt;

&lt;h2&gt;
  
  
  Solution Design
&lt;/h2&gt;

&lt;p&gt;The main focus of this project is to perform a regular check to your HTTPS endpoints and check the expiry date of your SSL certificates used by that endpoint. The full certificate object is being collected, in this implementation I am only reporting based on expiry dates but other checks can be added as well. The reason to use CI/CD pipelines hence agents is to allow operation teams to run these tests with runners/agents that has the network line of sight to both their public facing and internal origin endpoints.&lt;/p&gt;

&lt;p&gt;This tool will do a TCP based retrieval for the SSL certificate of remote endpoint and holds generates a report in JSON format. Afterwards this JSON report is used to generates a custom HTML report for publishing and to be used as a body of an HTML based notification. The pipelines will also generate a test report in JUnitXml for easier reporting of pipeline run status and reporting.&lt;/p&gt;

&lt;p&gt;The solution has a pipeline/workflow sample for both Azure DevOps Pipelines and Github Actions based on your choice of CI/CD tooling. Both has different capabilities but both are popular and valid choices.&lt;/p&gt;

&lt;p&gt;Customizable within the utility scripts, the current alerting thresholds and color codes are based on table below.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Alert Threshold in Days&lt;/th&gt;
&lt;th&gt;Severity&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;90&lt;/td&gt;
&lt;td&gt;&lt;span&gt;Low&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;60&lt;/td&gt;
&lt;td&gt;&lt;span&gt;Medium&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;30&lt;/td&gt;
&lt;td&gt;&lt;span&gt;High&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Azure DevOps Pipelines
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Cron Schedule, allowing for periodic checks, reporting and alerting&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://marketplace.visualstudio.com/items?itemName=JakubRumpca.azure-pipelines-html-report&amp;amp;targetId=c847560f-17a1-4ef2-a8c2-af353ae8846e&amp;amp;utm_source=vstsproduct&amp;amp;utm_medium=ExtHubManageList"&gt;HTML Viewer&lt;/a&gt; extension is used to view the HTML report as a pipeline result&lt;/li&gt;
&lt;li&gt;Pester Test results are reported in the pipeline for seeing if any of the endpoints checked has an SSL certificate with expiry date within alerting periods.&lt;/li&gt;
&lt;li&gt;The test results are also allows us to report on pipeline's result status. An easy way for getting notified from the pipeline can be achieved via subscribing to the pipeline's results in various ways Azure DevOps has native integration such as email, or Microsoft Teams Azure DevOps bot. &lt;/li&gt;
&lt;li&gt;Alternative e-mail based or webhook based notifications can be added via the utility script &lt;code&gt;Send-Notifications.ps1&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Azure DevOps project dashboard widgets such as test results trends or pipeline build result widget can be used to track status at a glance inside Azure DevOps.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Sample Azure DevOps Pipeline Run Screenshots
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Sample Azure DevOps Pipeline Run&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--FxmmG75K--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sjinrhf83lv35gqtmkb8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--FxmmG75K--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sjinrhf83lv35gqtmkb8.png" alt="AzureDevOpsPipeline_RunSample" width="880" height="303"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sample Azure DevOps Pipeline Run Test Results&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--eKLk96NG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jiqex6vhlg2ffxmwf5sv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--eKLk96NG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jiqex6vhlg2ffxmwf5sv.png" alt="AzureDevOpsPipeline_TestReportSample" width="880" height="464"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sample Azure DevOps Pipeline Run Custom HTML Report&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ZWQVeRDJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wbc3b4mvdq1zs6ckc590.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ZWQVeRDJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wbc3b4mvdq1zs6ckc590.png" alt="AzureDevOpsPipeline_CustomHTMLReportSample" width="880" height="316"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sample Azure DevOps Dashboard Sample&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5wYFGpwR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4gudjl5pgmq397knnk79.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5wYFGpwR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4gudjl5pgmq397knnk79.png" alt="AzureDevOpsPipeline_DashboardWidget_PipelineTrends" width="880" height="253"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  GitHub Actions
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Sample GitHub Actions Workflow Run Screenshots
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Cron Schedule, allowing for periodic checks, reporting and alerting&lt;/li&gt;
&lt;li&gt;As HTML Viewer extension is not available for GitHub Actions, publishing report to GitHub Pages has been used as an alternative. &lt;strong&gt;GitHub Pages requires either a public repository or a paid GitHub Account, if wished to be used in a private repository.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Pester Test results are reported in the pipeline for seeing if any of the endpoints checked has an SSL certificate with expiry date within alerting periods.&lt;/li&gt;
&lt;li&gt;The test results are also allows us to report on pipeline's result status. An easy way for getting notified from the pipeline can be achieved via subscribing to the pipeline's results in various ways Azure DevOps has native integration such as email, or Microsoft Teams HitHub integration bot.&lt;/li&gt;
&lt;li&gt;Alternative e-mail based or webhook based notifications can be added via the utility script &lt;code&gt;Send-Notifications.ps1&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Sample GitHub Actions Workflow Run&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--VDrChFXk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ozlw8a2qaiwfh9sqjoqj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--VDrChFXk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ozlw8a2qaiwfh9sqjoqj.png" alt="GitHubActionsWorkflow_RunSample" width="880" height="430"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sample GitHub Actions Workflow Run Test Results&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--I0HXg1Qi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s4wq6npikk6jwe56447n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--I0HXg1Qi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s4wq6npikk6jwe56447n.png" alt="GitHubActionsWorkflow_TestReportSampl" width="880" height="286"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sample GitHub Actions Workflow Run Custom HTML Report&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--nQlkVrdH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ih3fex8qecv5bnddozjk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nQlkVrdH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ih3fex8qecv5bnddozjk.png" alt="GitHubActionsWorkflow_CustomHTMLReportSample" width="692" height="385"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sample GitHub Actions Workflow Run Custom HTML Report Published to GitHub Pages&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zLNYzxQv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tcl9od0z4pkiu4d08ul3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zLNYzxQv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tcl9od0z4pkiu4d08ul3.png" alt="GitHubActionsWorkflow_CustomHTMLReportGitHubPageSample" width="880" height="224"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Usage
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Fork or Create your own Clone of this Repository&lt;/li&gt;
&lt;li&gt;Update &lt;code&gt;endpoints.json&lt;/code&gt; with your own endpoints&lt;/li&gt;
&lt;li&gt;Setup your GitHub Actions Workflow and/or Azure DevOps Pipeline in your repository or Azure DevOps project.&lt;/li&gt;
&lt;li&gt;*&lt;strong&gt;&lt;em&gt;Optional:&lt;/em&gt;&lt;/strong&gt;* Update the &lt;code&gt;alertThresholds.json&lt;/code&gt; with your own defined values. This config file defines how many days of expiry left are marked as Low, Medium and High alerting severities.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Optional:&lt;/strong&gt; If some of the endpoints in your list consists of network protected endpoints, not accessible to Microsoft hosted Azure DeVOps Pipelines or GitHub Actions Runners, please update the agent configuration as needed to use your self-hosted agents.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Optional:&lt;/strong&gt; Update the Cron Schedule expression as required.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Optional:&lt;/strong&gt; Update the &lt;code&gt;Send-Notifications.ps1&lt;/code&gt; script to directly send notifications from the pipeline. This may require you to also pass secret(s) to your pipeline in order to use the communication integrations.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Contributions &amp;amp; Issues
&lt;/h2&gt;

&lt;p&gt;Contributions are welcomed, please raise a Pull Request with your proposed modifications if you wish to make any changes.&lt;br&gt;
If you find an Issue and wish to report it, please use the Issues section.&lt;/p&gt;

&lt;h2&gt;
  
  
  License
&lt;/h2&gt;

&lt;p&gt;MIT Licensed, please see &lt;a href="https://github.com/MertSenel/ssl-certificate-expiry-checker/blob/main/LICENSE"&gt;license&lt;/a&gt; for details.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>powershell</category>
      <category>ssl</category>
      <category>cicd</category>
    </item>
    <item>
      <title>Practical PowerShell Scripting for DevOps - Part 4</title>
      <dc:creator>MertSenel</dc:creator>
      <pubDate>Fri, 22 Oct 2021 13:38:34 +0000</pubDate>
      <link>https://dev.to/mertsenel/practical-powershell-scripting-for-devops-part-4-35j7</link>
      <guid>https://dev.to/mertsenel/practical-powershell-scripting-for-devops-part-4-35j7</guid>
      <description>&lt;p&gt;Hi, I'm back with you with Part 4 of this series. On this part, I will show you how to check if a particular Git Reference belongs to a certain folder and also has a valid Semantic Versioning format. This type of script is a utility function that helps to fill gaps in particular automation scenario especially in deployment pipelines. Another common term used for these scripts is &lt;code&gt;Glue Scripts&lt;/code&gt;. Glue in this context means filling a gap in your toolchain with a custom script and achieve your goals. &lt;/p&gt;

&lt;p&gt;As DevOps engineers, we like to use complete and mature CI/CD tools. During my career I had used various CI/CD tools such as Jenkins, TeamCity, Azure DevOps, Octopus Deploy and Github Actions. Although all of these tools has rich repository of official and vendor provided tasks/actions almost all of them has common features to allow you run your own custom scripts. When pre-defined tasks comes short or you can't necessarily find something that does the trick out of the box, then you have go back to your biggest strength, writing your own custom &lt;code&gt;glue script(s)&lt;/code&gt; to fill in the gaps. &lt;/p&gt;

&lt;p&gt;Most of these CI/CD tools comes with a concept called &lt;code&gt;predefined variables&lt;/code&gt;. These are system generated environment variables that are passed to your runners/workers that allows you to tap into certain platform or repository related metadata depending on the unique pipeline run. Today we will be looking at some sample GitHub Reference strings and find out if they are under a certain folder/path and also has valid Semantic Versioning format. &lt;/p&gt;

&lt;p&gt;Semantic Versioning, or in short SemVer format, is a artifact versioning system that is widely accepted that allows engineers who write and maintain software in distinct versions. It is so widely used you've most likely came across this format in one shape or form. &lt;/p&gt;

&lt;p&gt;Let's say you are releasing a product or a software service for the first time, your typical version number for this particular release can be &lt;code&gt;1.0.0&lt;/code&gt; then let say your next release with some features add can be &lt;code&gt;1.1.0&lt;/code&gt;. Let say you've detected some defects and want to push some patches to second release, then you can name your new version as &lt;code&gt;1.1.1&lt;/code&gt;. Next release would be &lt;code&gt;1.2.0&lt;/code&gt; and so on. &lt;/p&gt;

&lt;p&gt;Going from &lt;code&gt;1.x.x&lt;/code&gt; to &lt;code&gt;2.x.x&lt;/code&gt; denotes a major version upgrade, of then denotes changes that are &lt;code&gt;MAJOR&lt;/code&gt; and to point out that you are doing changes which contains good amount of change in the software. This is the simplest explanation of Semantic versioning and I can't possibly explain how Semantic Versioning works fully but I urge you to check out this page to fully understand it and google some more to learn about it. &lt;a href="https://semver.org/" rel="noopener noreferrer"&gt;https://semver.org/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I'm going to give examples from GitHub and Azure DevOps but I'm almost certain that all CI/CD tool that works with Git repositories would have something similar. &lt;/p&gt;

&lt;p&gt;Check out the pages for each product for predefined/environment variables available to you in their pipelines contexts. &lt;/p&gt;

&lt;p&gt;Azure DevOps: &lt;a href="https://docs.microsoft.com/en-us/azure/devops/pipelines/build/variables?view=azure-devops&amp;amp;tabs=yaml" rel="noopener noreferrer"&gt;https://docs.microsoft.com/en-us/azure/devops/pipelines/build/variables?view=azure-devops&amp;amp;tabs=yaml&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fih6mfweqtrnpnqv8b3m3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fih6mfweqtrnpnqv8b3m3.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;GitHub Actions &lt;a href="https://docs.github.com/en/actions/learn-github-actions/environment-variables" rel="noopener noreferrer"&gt;https://docs.github.com/en/actions/learn-github-actions/environment-variables&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foyfun3onncy435npvain.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foyfun3onncy435npvain.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To simulate that we have one of these variable available to us in our script context, we will start of with a sample input array.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$refsInputArray&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;@(&lt;/span&gt;&lt;span class="s2"&gt;"refs/heads/releases/1.0.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="s2"&gt;"refs/heads/releases/1.a.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="s2"&gt;"refs/heads/releases/1.2.0.5.6.9"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="s2"&gt;"refs/heads/release/1.1.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="s2"&gt;"refs/heads/releases/1.0.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="s2"&gt;"refs/heads/releases/2.4.3"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="s2"&gt;"refs/heads/main"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="s2"&gt;"refs/heads/develop"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, we don't get to choose the format we will receive our String Inputs, hence we need to know how to get certain parts of interest in a complex string like this and then perform our checks. &lt;/p&gt;

&lt;p&gt;Without further due, lets get in to our problem statement and requirements in a real use case DevOps scenario.&lt;/p&gt;

&lt;h2&gt;
  
  
  Problem
&lt;/h2&gt;

&lt;p&gt;Hey fellow DevOps Engineer! We have a pipeline for an API service we have, and we have a delivery pipeline that allows us to deploy our code to our serverless hosting platform from a particular branch of our choosing.&lt;/p&gt;

&lt;p&gt;Although we give our developers flexibility to deploy to all environments from any feature branch they choose to deploy from, we would like to protect our &lt;code&gt;Production&lt;/code&gt; environment. We have strict &lt;code&gt;branch protection&lt;/code&gt; rules around &lt;code&gt;releases/*&lt;/code&gt; branches and only wish to be able to deploy from these branches to our &lt;code&gt;Production&lt;/code&gt; workers. We also use Semantic versioning to version our &lt;code&gt;releases&lt;/code&gt; and cut our release branches with the version number of that particular release.&lt;/p&gt;

&lt;p&gt;We've look into our CI/CD tool's out of the box and marketplace tasks, but couldn't find anything particular that can achieve what we are looking for, hence we need your help. &lt;/p&gt;

&lt;p&gt;We would like you to write us a &lt;code&gt;PowerShell&lt;/code&gt; script to perform a simple test to verify if the &lt;code&gt;Git Branch&lt;/code&gt; we are running the pipeline from is actually adheres to our release versioning rules, this would save us from deploying the wrong branch to the &lt;code&gt;Production&lt;/code&gt; environment.&lt;/p&gt;

&lt;p&gt;1- We would like you to first check if the &lt;code&gt;branch&lt;/code&gt; we are deploying from is under &lt;code&gt;releases/&lt;/code&gt; folder.&lt;/p&gt;

&lt;p&gt;2- We also like to check if the branch name actually adheres to Semantic Versioning format.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Success Looks like for this Challenge
&lt;/h2&gt;

&lt;p&gt;Normally, each pipeline run would have singular input for a Git Reference hence the script would normally finish execution with a proper exit code. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tip&lt;/strong&gt;: &lt;code&gt;exit 0&lt;/code&gt; means &lt;code&gt;successful&lt;/code&gt; execution and &lt;code&gt;exit 1&lt;/code&gt; means an exection with an unhandled exception or in other words a &lt;code&gt;failure&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;But because we are doing a mock challenge, in this example, I want you to start your script with the sample input array I've provided above and then simply return an appropriate message to the terminal stating if the input string fulfills the requirements or not.&lt;/p&gt;

&lt;p&gt;Successful Run:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyzcapjyb4vsy8oaa80va.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyzcapjyb4vsy8oaa80va.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Some Pointers For Writing Your Own Solution
&lt;/h2&gt;

&lt;p&gt;So let's breakdown the challenge a little bit. First of all we need to identify the folder of the branch. &lt;/p&gt;

&lt;p&gt;In order to do this, we need the portion of the substring between the last two &lt;code&gt;/&lt;/code&gt; characters. There may be multiple way of doing this so try to figure out how to achieve this first, once you have that portion then you can actually perform a string comparison to check if it's equal to &lt;code&gt;releases&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;If your input passed the first requirement, then basically you need to capture the substring after the last &lt;code&gt;/&lt;/code&gt; character. Once you have that then you can't simply do a string comparison as semantic version number can be multiple things, we want to check if it adheres to a pattern. &lt;/p&gt;

&lt;p&gt;PowerShell being a scripting language has operators to make your life easy and allow you to achieve this in various ways, you can devise a &lt;code&gt;PowerShell&lt;/code&gt; flavored solution. The most bulletproof method is to use something called &lt;code&gt;regular expressions&lt;/code&gt; &lt;/p&gt;

&lt;p&gt;At the bottom of the page I've provided above on semantic versioning they also provide regular expression to check if a certain string adheres to semantic versioning. &lt;/p&gt;

&lt;p&gt;I'm using the second one in section &lt;a href="https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string" rel="noopener noreferrer"&gt;https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;^&lt;span class="o"&gt;(&lt;/span&gt;0|[1-9]&lt;span class="se"&gt;\d&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;0|[1-9]&lt;span class="se"&gt;\d&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;0|[1-9]&lt;span class="se"&gt;\d&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="o"&gt;)(&lt;/span&gt;?:-&lt;span class="o"&gt;((&lt;/span&gt;?:0|[1-9]&lt;span class="se"&gt;\d&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt;|&lt;span class="se"&gt;\d&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;a-zA-Z-][0-9a-zA-Z-]&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="o"&gt;)(&lt;/span&gt;?:&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;?:0|[1-9]&lt;span class="se"&gt;\d&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt;|&lt;span class="se"&gt;\d&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;a-zA-Z-][0-9a-zA-Z-]&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;?&lt;span class="o"&gt;(&lt;/span&gt;?:&lt;span class="se"&gt;\+&lt;/span&gt;&lt;span class="o"&gt;([&lt;/span&gt;0-9a-zA-Z-]+&lt;span class="o"&gt;(&lt;/span&gt;?:&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;0-9a-zA-Z-]+&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;?&lt;span class="err"&gt;$&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also script you own logic to check is this particular string you have adheres to the format by looking at it's components and if it has numerical values separated by dots in between. However, learning regular expression will help you in various string grouping and filtering scenarios in your cloud and DevOps career so I really urge you to learn and try to understand what they are and how they are used. &lt;/p&gt;

&lt;p&gt;Event most seasoned engineers are struggling with regular expressions and so don't be discouraged if they appear to you as too complex at first. Even a small bit of knowledge goes a long way.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Main Learnings From the Challenge
&lt;/h3&gt;

&lt;p&gt;The main learnings from this challenge are:&lt;/p&gt;

&lt;p&gt;1- PowerShell comparison operators&lt;/p&gt;

&lt;p&gt;2- Regular Expressions&lt;/p&gt;

&lt;p&gt;3- PowerShell String operator &lt;code&gt;split&lt;/code&gt; to divide a certain string into substrings&lt;/p&gt;

&lt;p&gt;4- PowerShell Arrays and how to target a particular Array Index&lt;/p&gt;

&lt;p&gt;Links:&lt;br&gt;&lt;br&gt;
&lt;a href="https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_comparison_operators?view=powershell-7.1#matching-operators" rel="noopener noreferrer"&gt;Matching operators&lt;/a&gt;  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_regular_expressions?view=powershell-7.1" rel="noopener noreferrer"&gt;about_Regular_Expressions&lt;/a&gt;  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_split?view=powershell-7.1" rel="noopener noreferrer"&gt;about_Split&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_arrays?view=powershell-7.1" rel="noopener noreferrer"&gt;about_Arrays&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Tasks You Need To Perform In Your Script
&lt;/h3&gt;

&lt;p&gt;So to give another breakdown of what you need to do in your script,&lt;/p&gt;

&lt;p&gt;1- Loop through the input array to check each input string individually&lt;/p&gt;

&lt;p&gt;2- Figure out a way to isolate the substring between the last two &lt;code&gt;/&lt;/code&gt; characters check if it's equal to &lt;code&gt;releases&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;3- Figure out a way to isolate the substring after the last &lt;code&gt;/&lt;/code&gt; character and check if it has a valid Semantic Versioning format (I recommend using a regular expression/regex pattern to perform this as it would also familiarise yourself with regex)&lt;/p&gt;

&lt;p&gt;4- Print out relevant log messages to terminal that allows you to clearly see the input being evaluated and on failure, what requirement it fails and if satisfies both conditions a relevant success message. (Normally you would either fail or proceed with the pipeline runs according to the result but just printing a message is fine for this mock challenge)&lt;/p&gt;
&lt;h2&gt;
  
  
  Final Tips
&lt;/h2&gt;

&lt;p&gt;From this point below you will see my sample solution. Depending on your level and learning style, either take a look before or after you gave it your shot.&lt;/p&gt;
&lt;h2&gt;
  
  
  My Sample Solution
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Files for my Sample Solution
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/MertSenel/devops-powershell/tree/main/scripts/local/practical-powershell-scripting-for-devops-part-4/SemverControl/Part4" rel="noopener noreferrer"&gt;Part-4 Files&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Test-SemVerWithRegex.ps1&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$refsInputArray&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;@(&lt;/span&gt;&lt;span class="s2"&gt;"refs/heads/releases/1.0.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="s2"&gt;"refs/heads/releases/1.a.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="s2"&gt;"refs/heads/releases/1.2.0.5.6.9"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="s2"&gt;"refs/heads/release/1.1.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="s2"&gt;"refs/heads/releases/1.0.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="s2"&gt;"refs/heads/releases/2.4.3"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="s2"&gt;"refs/heads/main"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="s2"&gt;"refs/heads/develop"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c"&gt;#https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$semVerRegex&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$'&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="nv"&gt;$refsInputArray&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ForEach-Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nv"&gt;$refParts&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;$PSItem&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-split&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'/'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$refParts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;-2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-eq&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'releases'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nv"&gt;$refParts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;-1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-match&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$semVerRegex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="n"&gt;Write-Host&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Reference &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="bp"&gt;$PSItem&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt; is in releases folder and has a correct semver format"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="kr"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="n"&gt;Write-Error&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Reference &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="bp"&gt;$PSItem&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt; is in releases folder but has an incorrect semver format"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="kr"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;Write-Error&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Reference &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="bp"&gt;$PSItem&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt; is not in releases folder"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is my solution and as I've stated during this part, there are multiple way of structuring your script and achieving the asked outputs. Only logic you are writing matters here, you can choose to have one complex if statement, you can use one complex conditional or opt-in for a switch statement for some more challenge, as long as you are fulfilling the challenge requirements you've accomplished the learning goals of this challenge.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sample Runs
&lt;/h3&gt;

&lt;p&gt;As our script is deterministic, it should always return the same result with the given input.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyzcapjyb4vsy8oaa80va.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyzcapjyb4vsy8oaa80va.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;No matter, the purpose, the fundamental and primitive data types such as strings and arrays are things you will come across in various context no matter what environment you work in. Having a good understanding about these concepts are always beneficial and will allow you to increase your speed in devising solutions for complex problems.&lt;/p&gt;

&lt;p&gt;Strings in particular plays a crucial role in DevOps scripting, Infrastructure as Code and pipeline automation as we usually parameterize certain templates and provide string based parameter inputs to those templates to achieve scalable automation solutions. Parametrizing templates and promoting along the software development lifecycle environments with one consistent template with different parameters will give you confidence in going to &lt;code&gt;Production&lt;/code&gt; as you will be sure that you are deploying and testing the same set of resources in your lower environments. &lt;/p&gt;

&lt;p&gt;This is a mock challenge to give you a medium to learn the skills to be able to work with string and arrays with Powershell, but as you continue along you will see they will come in handy in all type of different situations. I hope I was able to help you learn something new about PowerShell today, and I'll see you on the next one.&lt;/p&gt;

</description>
      <category>powershell</category>
      <category>devops</category>
      <category>scripting</category>
    </item>
    <item>
      <title>Practical PowerShell Scripting for DevOps - Part 3</title>
      <dc:creator>MertSenel</dc:creator>
      <pubDate>Sun, 19 Sep 2021 04:06:49 +0000</pubDate>
      <link>https://dev.to/mertsenel/practical-powershell-scripting-for-devops-part-3-3k9i</link>
      <guid>https://dev.to/mertsenel/practical-powershell-scripting-for-devops-part-3-3k9i</guid>
      <description>&lt;p&gt;Hi, I'm back with you with Part 3 of this series. On this part, I will show you how we can run loops in parallel for operations that doesn't have any racing conditions or dependencies on each other. This is a fairly new feature introduced with PowerShell 7.1+ and it's most useful to speed up your scripts. As this is a continuation of a series I highly recommend reading &lt;a href="https://dev.to/post/practical-powershell-scripting-for-devops-part-1"&gt;Part-1&lt;/a&gt; and &lt;a href="https://dev.to/post/practical-powershell-scripting-for-devops-part-2"&gt;Part-2&lt;/a&gt; first.&lt;/p&gt;

&lt;p&gt;As I like to combine different concepts together, I will also combine the parallel loop approach with "Jobs" to show you how to make your scripts "Asynchronous". Combined, these concepts can be reused in various scenarios to help you write smarter and faster scripts.&lt;/p&gt;

&lt;p&gt;As we are looking for performance improvements in terms of execution time, I'll also include a method you can use to measure and display execution times of your powershell scripts. &lt;/p&gt;

&lt;h2&gt;
  
  
  Problem
&lt;/h2&gt;

&lt;p&gt;Hey fellow DevOps Engineer! The last script improvement was magnificent, no more false positive failures with the retry mechanism you've implemented. However we now have another problem! As we've introduced a retry mechanism into our HttpTest function, the script started to take a lot of time execute as each website is tested sequentially. The impact is felt more as our company grew further and now we have more endpoints we wish to perform these tests on. &lt;/p&gt;

&lt;p&gt;1- We don't care in which order we got our test results as long as we have the results as fast as possible.&lt;/p&gt;

&lt;p&gt;2- The worker infrastructure we are running these tests on are powerful enough to run 50 tests at a time.&lt;/p&gt;

&lt;p&gt;3- We wish to re-use helper function "New-HttpTestResult" as is or with little modification as possible. &lt;/p&gt;

&lt;p&gt;4- Can you please add another output at the end of the script to display the Total duration of the full script execution, this will help us confirm the performance improvements. &lt;/p&gt;

&lt;h2&gt;
  
  
  What Success Looks like for this Challenge
&lt;/h2&gt;

&lt;p&gt;I've quickly added the Timer feature to our Part-2 Version of the script which doesn't have our new and improved parallel loop. &lt;/p&gt;

&lt;p&gt;To fully appreciate the performance gains we will achieve from our improvement, I've also doubled our Test.json file to Test 18 endpoints. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F32rlb6xeeedenhhaey9u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F32rlb6xeeedenhhaey9u.png" alt="Part-2 version of Script Output with Timer Added and more Test Endpoints"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As it can be seen from the screenshot, the total execution time is 113 seconds, it's quite a long time to wait, but this version of the script's loop is working in sequence and is synchronous by design, it has to wait for test at hand to go to the next one. &lt;/p&gt;

&lt;p&gt;Here is the improved version: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foh4q6lw67mie27xn0zsk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foh4q6lw67mie27xn0zsk.png" alt="Part-3 version of Script Uses a Parallel Loop and PowerShell Thread Jobs"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;First of all, same amount of endpoints only took 20 seconds to test with this version of the script. Another thing to notice is that, the order of results are not same as the order of our input, but instead it's in the order of test response return time. &lt;/p&gt;

&lt;p&gt;As you can see, the tests which required more retries, hence took more time are displayed further down the list, whereas endpoints which returned a successfully result on first attempts are at the top. &lt;/p&gt;

&lt;p&gt;Back to our original point, new version have improved the script's execution duration from 113 seconds to 20 seconds, it's an x5 times improvement. This will scale even further if the input list gets bigger.&lt;/p&gt;

&lt;h2&gt;
  
  
  Some Pointers For Writing Your Own Solution
&lt;/h2&gt;

&lt;p&gt;This can be your starting point Part-2 Files of my sample solution (With Timer added)&lt;br&gt;
&lt;a href="https://github.com/MertSenel/devops-powershell/tree/main/scripts/local/practical-powershell-scripting-for-devops-part-3/HttpPingTest/Part2-Files-Modified-with-Timer" rel="noopener noreferrer"&gt;Part-2 Files&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Some Test Endpoints You Can Use
&lt;/h3&gt;

&lt;p&gt;As this part I'll provide the starting point, you will already have the curated test endpoints json file readily available. It has multiple endpoints for simulating different scenarios. For more information please read &lt;a href="https://dev.to/post/practical-powershell-scripting-for-devops-part-1/"&gt;part 1&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  The Main Learning From the Challenge
&lt;/h3&gt;

&lt;p&gt;We are using new parallel feature of the ForEach-Object cmdlet and Thread Jobs which are another feature of the new PowerShell 7. &lt;/p&gt;

&lt;p&gt;So I recommend reading and understanding these concepts from the documentation. &lt;/p&gt;

&lt;p&gt;Finally, parallel foreach loops doesn't support running custom functions defined outside of the loop, as each iteration of the loop is a new runspace. Hence we need to define our helper function in each of the loop iteration. Currently, cmdlet doesn't supports this natively but there is a very easy workaround I've found on stackoverflow and sharing with you so you don't have to look for it. &lt;/p&gt;

&lt;p&gt;The main idea is to, import your function a plain string, and then actually creating the function definition inside the loop iteration via using this string as source. Then you would be able to use your custom function(s). &lt;/p&gt;

&lt;p&gt;I've used Stopwatch class to measure the script execution time.  Stopwatch has been created via using the built-in class of .NET Core. &lt;br&gt;
PowerShell can create objects from these classes via a syntax called Type Accelerator. &lt;/p&gt;

&lt;p&gt;Links:&lt;br&gt;&lt;br&gt;
&lt;a href="https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/foreach-object?view=powershell-7.1" rel="noopener noreferrer"&gt;ForEach-Object&lt;/a&gt; &lt;br&gt;
&lt;a href="https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_thread_jobs?view=powershell-7.1" rel="noopener noreferrer"&gt;about_Thread_Jobs&lt;/a&gt;  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://stackoverflow.com/questions/61273189/how-to-pass-a-custom-function-inside-a-foreach-object-parallel" rel="noopener noreferrer"&gt;How_to_Run_Custom_Function_in_Parallel_ForEach_Loop&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.stopwatch?view=netcore-3.1" rel="noopener noreferrer"&gt;StopWatch Class&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_type_accelerators?view=powershell-7.1" rel="noopener noreferrer"&gt;about_Type_Accelerators&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Tasks You Need To Perform In Your Script
&lt;/h3&gt;

&lt;p&gt;1- Convert the &lt;code&gt;foreach&lt;/code&gt; loop we had in Part-2 script to it's &lt;code&gt;ForEach-Object -Parallel&lt;/code&gt; implementation. &lt;/p&gt;

&lt;p&gt;2- Be able to reference and use your custom New-HttpTestResult function inside the parallel foreach-object loop. &lt;/p&gt;

&lt;p&gt;3- Make sure you are displaying results as they come, rather than waiting for all tests to finish executing. You will need to implement Powershell Thread Jobs to achieve this behaviour.&lt;/p&gt;

&lt;p&gt;4- Add a mechanism to measure and display the total execution time of the script&lt;/p&gt;
&lt;h2&gt;
  
  
  Final Tips
&lt;/h2&gt;

&lt;p&gt;From this point below you will see my sample solution. Depending on your level and learning style, either take a look before or after you gave it your shot.&lt;/p&gt;
&lt;h2&gt;
  
  
  My Sample Solution
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Folder Structure
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzoqyb5qm9pzh4ynxqsst.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzoqyb5qm9pzh4ynxqsst.png" alt="Project Folder Structure"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Files for my Sample Solution
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/MertSenel/devops-powershell/tree/main/scripts/local/practical-powershell-scripting-for-devops-part-3/HttpPingTest/Part3-Files" rel="noopener noreferrer"&gt;Part-3 Files&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Tests.json&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This is the list of endpoints I would like to test, stored in JSON format which can be produced and consumed with a variety of modern languages.&lt;br&gt;
What we have here is an array of objects that has name and url keys for us to perform and label the tests.&lt;/p&gt;

&lt;p&gt;As this file has been covered in last two parts I won't be providing more explanation.&lt;/p&gt;

&lt;p&gt;Our Helper Function, create this file under path &lt;code&gt;./lib/New-HttpTestResult.ps1&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;As this file has been covered in last two parts I won't be providing more explanation.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Test-HttpEndpoints.ps1&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Our Main script, and all the changes we've performed are on this script for Part-3 of the series. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;I've introduced a Stopwatch object. Stopwatch has been created via using the built-in class of .NET Core. &lt;br&gt;
&lt;a href="https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.stopwatch?view=netcore-3.1" rel="noopener noreferrer"&gt;StopWatch Class&lt;/a&gt;&lt;br&gt;
PowerShell can create objects from these classes via a syntax called Type Accelerator. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Our foreach loop, now converted into a ForEach-Object Parallel version and we are running each operation as a Job.&lt;br&gt;
Hence, now the result of our loop is not a collection of test results, but instead a collection of background jobs that have been started in parallel.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Now that we have the collection of Job created, we can wait for the results of these jobs. The results of these jobs, will be result of a HttpTestResult.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;From there on, we print the test results as each job is finish running and gets completed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;CmdletBinding&lt;/span&gt;&lt;span class="p"&gt;()]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="kr"&gt;param&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Parameter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ValueFromPipeline&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;$true&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nv"&gt;$TestsFilePath&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s1"&gt;'.\Tests.json'&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c"&gt;#Lets Create a StopWatch Object.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$Stopwatch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;System.Diagnostics.Stopwatch&lt;/span&gt;&lt;span class="p"&gt;]::&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c"&gt;# Now Start the timer.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$Stopwatch&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c"&gt;# Convert JSON Config Files String value to a PowerShell Object&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$TestsObj&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Get-Content&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$TestsFilePath&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ConvertFrom-Json&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c"&gt;# Import the Tester Function&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;/lib/New-HttpTestResult.ps1&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="nv"&gt;$funcDef&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;${function:New-HttpTestResult}&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="nv"&gt;$jobs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$TestsObj&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ForEach-Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Parallel&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="nv"&gt;${function:New-HttpTestResult}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$&lt;/span&gt;&lt;span class="nn"&gt;using&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;funcDef&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="n"&gt;New-HttpTestResult&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-TestArgs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;$_&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-AsJob&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-ThrottleLimit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="nv"&gt;$jobs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Receive-Job&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Wait&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Format-Table&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c"&gt;# Now Stop the timer.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$Stopwatch&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Stop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="nv"&gt;$TestDuration&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;$Stopwatch&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Elapsed&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;TotalSeconds&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="n"&gt;Write-Host&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Total Script Execution Time: &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nv"&gt;$TestDuration&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt; Seconds"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Sample Runs
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foh4q6lw67mie27xn0zsk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foh4q6lw67mie27xn0zsk.png" alt="Part-3 version of Script Uses a Parallel Loop and PowerShell Thread Jobs"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The biggest benefit of Automation is that it has the ability to scale. However, while writing our scripts, we also need to consider the input it will take and the nature of operation we are performing. &lt;/p&gt;

&lt;p&gt;Running loops in parallel, and using background jobs instead of waiting the execution of each operation is a way to speed up automation workflows. Imagine you are responsible for writing an automation to shutdown 10s of VMs on Friday evening. Would it really make sense to write a script that shuts them down, one at a time? What if that number was around 1000s, how would your automation scale? &lt;/p&gt;

&lt;p&gt;Where possible, I tend to now leverage parallel loops to speed up execution and avoid unnecessary waiting in my scripts. Of course not each scenario can work with this type of loop, but deciding when you need it is also on you as the engineer. It's another tool in our toolkit, it's now up to us to decide where and how to use it.&lt;/p&gt;

</description>
      <category>powershell</category>
      <category>devops</category>
      <category>scripting</category>
    </item>
    <item>
      <title>Practical PowerShell Scripting for DevOps - Part 2</title>
      <dc:creator>MertSenel</dc:creator>
      <pubDate>Sun, 23 May 2021 12:56:55 +0000</pubDate>
      <link>https://dev.to/mertsenel/practical-powershell-scripting-for-devops-part-2-4cn5</link>
      <guid>https://dev.to/mertsenel/practical-powershell-scripting-for-devops-part-2-4cn5</guid>
      <description>&lt;p&gt;On this part, I will be extending the monitoring script we wrote on Part-1, with a polling while loop&lt;/p&gt;

&lt;p&gt;Hi, I'm back with you with Part 2 of this series. On this part, I will ask you to extend the http ping monitoring script we wrote on Part-1. If you've completed Part-1 challenge then you extend your own script as well. In any case I will be providing my version of Part-1 solution as a starting point. Ok, lets jump on the our problem statement for this challenge. &lt;/p&gt;

&lt;h2&gt;
  
  
  Problem
&lt;/h2&gt;

&lt;p&gt;Hey fellow DevOps Engineer! We loved the first script you wrote for us, now we can test the health of 10s of our endpoints with ease with just a configuration file. We now have a problem though, we can see that lot's of failures appears to be intermittent, as in if we simply retry couple of more times we will eventually get a successful result. We believe this behaviour is happening either due to a server warming up after a new deployment or some other delay throughout the network connectivity. &lt;/p&gt;

&lt;p&gt;To filter out these FALSE positive failures, we would like the new version of this script to have a retry mechanism, so we can truly find out if a service is down. However, last time another engineer setup something similar, which we were running on a CI/CD pipeline, and for a unhealthy endpoint pipeline run would never end, costing our company a lot of money due to CI/CD agent hour costs. &lt;/p&gt;

&lt;p&gt;So you need to make sure your solution meets the below requirements: &lt;/p&gt;

&lt;p&gt;1- Retry the test for a maximum of N amount of times (configurable) this endpoint if the first attempt does not return a HTTP 200 response&lt;/p&gt;

&lt;p&gt;2- Capture the attempt number of successfully response, if endpoint ever returns success code&lt;/p&gt;

&lt;p&gt;3- Wait X amount of seconds in between retry attempts&lt;/p&gt;

&lt;p&gt;4- Move on to the next endpoint if the maximum retry amount is reached &lt;/p&gt;

&lt;p&gt;5- Prints results to terminal in a human readable format&lt;/p&gt;

&lt;h2&gt;
  
  
  What Success Looks like for this Challenge
&lt;/h2&gt;

&lt;p&gt;The script I wrote on Part-1 were printing a result similar to: &lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh0gca753nqyxcdh8nmtu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh0gca753nqyxcdh8nmtu.png" alt="Part-1 version of Script Output"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now we want our script to print a result similar to below: &lt;br&gt;
Now on the first run of my script, I've configured the max retry parameter's value as 2, hence only with 2 retries not all of the flaky sites returned a successful result. &lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkotf179l59b6k524ybbr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkotf179l59b6k524ybbr.png" alt="Part-2 Sample Successful Output 1"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Increasing the max retry count to 10, we can see more endpoints now returning successful responses, on retry attempts. In this run for example FlakyWebsite-#4 endpoint return a success code on 9th attempt. &lt;/p&gt;

&lt;p&gt;In both runs FaultyWebsite-#1 does not return a success code (as it's supposed to) and our script polls this endpoint for maximum amount of times.&lt;br&gt;
Our solution works as expected. Great!&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcvykf9a4ib5mv7q00jvd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcvykf9a4ib5mv7q00jvd.png" alt="Part-2 Sample Successful Output 2"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Some Pointers For Writing Your Own Solution
&lt;/h2&gt;

&lt;p&gt;This can be your starting point Part-1 Files of my sample solution&lt;br&gt;
&lt;a href="https://github.com/MertSenel/devops-powershell/tree/main/scripts/local/practical-powershell-scripting-for-devops-part-2/HttpPingTest/Part1-Files" rel="noopener noreferrer"&gt;Part-1 Files&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Some Test Endpoints You Can Use
&lt;/h3&gt;

&lt;p&gt;As this part I'll provide the starting point, you will already have the curated test endpoints json file readily available. It has multiple endpoints for simulating different scenarios. For more information please read &lt;a href="https://mertsenel.tech/post/practical-powershell-scripting-for-devops-part-1/" rel="noopener noreferrer"&gt;part 1&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  The Main Learning From the Challenge
&lt;/h3&gt;

&lt;p&gt;Polling is a well known programming concept. You check state of a system, evaluate the result and make your next move. We can use loops to implement this retry logic in our script. However, in order to avoid the concept of "infinite loop" we need to make sure we are also implementing some limits and proper &lt;code&gt;break&lt;/code&gt; conditions for our loop.&lt;/p&gt;

&lt;p&gt;I recommended reading the Powershell documentation for while loop and break from their docs. &lt;br&gt;
Links:&lt;br&gt;&lt;br&gt;
&lt;a href="https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_while?view=powershell-7.1" rel="noopener noreferrer"&gt;about_while&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_break?view=powershell-7.1" rel="noopener noreferrer"&gt;about_break&lt;/a&gt;  &lt;/p&gt;

&lt;p&gt;I'm using a while loop for these type of scenarios, but other loop types will work too. It will only change your implementation but same result should be achievable.  &lt;/p&gt;
&lt;h3&gt;
  
  
  Tasks You Need To Perform In Your Script
&lt;/h3&gt;

&lt;p&gt;1- Initialize Parameters/Variables for the Inputs you need. You need two new input, one for Maximum Amount of Retries and another one for Time to Wait in between retries. &lt;/p&gt;

&lt;p&gt;2- Put testing part of your code in a loop, after your test evaluate the result with a conditional statement (hint: if/else block) and then either exit the while loop (hint: &lt;code&gt;break&lt;/code&gt;) &lt;/p&gt;

&lt;p&gt;of if a retry is needed, wait for the configured amount of time before making the next test attempt. &lt;/p&gt;

&lt;p&gt;3- Initialize a counter variable and keep track of the no of attempt you are currently trying. If your test attempt is successful, you need to pass this attempt no back to the main script. Basically we want to if endpoint returned successful response after how many attempts. Example: 4/5 4 being the attempt number that we got a successful response and 5 being the maximum count of retries we would like to make.&lt;/p&gt;
&lt;h2&gt;
  
  
  Final Tips
&lt;/h2&gt;

&lt;p&gt;From this point below you will see my sample solution. Depending on your level and learning style, either take a look before or after you gave it your shot.&lt;/p&gt;
&lt;h2&gt;
  
  
  My Sample Solution
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Folder Structure
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbm9i65vn27mg8ds3vaj8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbm9i65vn27mg8ds3vaj8.png" alt="Project Folder Structure"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Files for my Sample Solution
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/MertSenel/devops-powershell/tree/main/scripts/local/practical-powershell-scripting-for-devops-part-2/HttpPingTest/Part1-Files" rel="noopener noreferrer"&gt;Part-1 Files&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Tests.json&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This is the list of endpoints I would like to test, stored in JSON format which can be produced and consumed with a variety of modern languages.&lt;br&gt;
What we have here is an array of objects that has name and url keys for us to perform and label the tests.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"FlakyWebsite-#1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://httpbin.org/status/200,403,404,500"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"StableWebsite-#1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://httpbin.org/status/200"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"FlakyWebsite-#2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://httpbin.org/status/200,403,404,500"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"StableWebsite-#2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://httpbin.org/status/200"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"StableWebsite-#3"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://httpbin.org/status/200"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"StableWebsite-#4"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://httpbin.org/status/200"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"FlakyWebsite-#3"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://httpbin.org/status/200,403,404,500"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"FlakyWebsite-#4"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://httpbin.org/status/200,403,404,500"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"FaultyWebsite-#1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://httpbin.org/status/500"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our Helper Function, create this file under path &lt;code&gt;./lib/New-HttpTestResult.ps1&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This script will perform an HTTP Ping Test with passed parameters and returns a Test Result Object&lt;/p&gt;

&lt;p&gt;Notice the difference from the version in Part-1, this version has 2 new parameters and a while loop, to retry the test if &lt;/p&gt;

&lt;p&gt;a non success code returns from the earlier responses. This is the same code you would write to poll anything, you would only change the code&lt;/p&gt;

&lt;p&gt;that performs the test. The test can be a &lt;code&gt;kubectl&lt;/code&gt; command that grabs a number of pods from a deployment replica set, and you would like to wait &lt;/p&gt;

&lt;p&gt;for all containers are in ready state after a manual upscale in number of pods. Or you can perform a SQL connection or TCP port ping to validate &lt;/p&gt;

&lt;p&gt;health in another type of non HTTP service. Possibilities are endless, but the main structure of the code will remain the same.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="kr"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;New-HttpTestResult&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="kr"&gt;param&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Parameter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ValueFromPipeline&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;$true&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;PSCustomObject&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nv"&gt;$TestArgs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="c"&gt;# Maximum Retry Amount&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Parameter&lt;/span&gt;&lt;span class="p"&gt;()][&lt;/span&gt;&lt;span class="n"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;$MaxRetryNo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="c"&gt;# Time to wait in between retry attempts&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Parameter&lt;/span&gt;&lt;span class="p"&gt;()][&lt;/span&gt;&lt;span class="n"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;$WaitTimeInSeconds&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nv"&gt;$ProgressPreference&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'SilentlyContinue'&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="nv"&gt;$Method&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'Get'&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="nv"&gt;$TestCounter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; 

    &lt;/span&gt;&lt;span class="c"&gt;# -lt: Lower Than &lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="kr"&gt;while&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$TestCounter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-lt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$MaxRetryNo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;

        &lt;/span&gt;&lt;span class="c"&gt;#Increment our counter by 1 before we make our first attempt&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nv"&gt;$TestCounter&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nv"&gt;$duration&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Measure-Command&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nv"&gt;$Response&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Invoke-WebRequest&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Uri&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$TestArgs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;url&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Method&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$Method&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-SkipHttpErrorCheck&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

        &lt;/span&gt;&lt;span class="c"&gt;# If we find the 200 code we stop polling&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$Response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;StatusCode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-eq&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'200'&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="kr"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="kr"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="c"&gt;#Else we need to wait for configured amount of time&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="n"&gt;Start-Sleep&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Seconds&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$WaitTimeInSeconds&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;PSCustomObject&lt;/span&gt;&lt;span class="p"&gt;]@{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="w"&gt;               &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$TestArgs&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nx"&gt;status_code&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$Response&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;StatusCode&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ToString&lt;/span&gt;&lt;span class="err"&gt;()&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nx"&gt;status_description&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$Response&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;StatusDescription&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nx"&gt;attempt_no&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nv"&gt;$TestCounter&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nv"&gt;$MaxRetryNo&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nx"&gt;responsetime_ms&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$duration&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Milliseconds&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nx"&gt;timestamp&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;get&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;date&lt;/span&gt;&lt;span class="err"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;ToString&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'O'&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;



    &lt;/span&gt;&lt;span class="kr"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;Test-HttpEndpoints.ps1&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Our Main script, we will invoke this script to run the tests and produce and returns a test results array&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;CmdletBinding&lt;/span&gt;&lt;span class="p"&gt;()]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="kr"&gt;param&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Parameter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ValueFromPipeline&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;$true&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nv"&gt;$TestsFilePath&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s1"&gt;'.\Tests.json'&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c"&gt;# Convert JSON Config Files String value to a PowerShell Object&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$TestsObj&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Get-Content&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$TestsFilePath&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ConvertFrom-Json&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c"&gt;# Import the Tester Function&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;/lib/New-HttpTestResult.ps1&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c"&gt;# Loop through Test Objects and get the results as a collection&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$TestResults&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;foreach&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$Test&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$TestsObj&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; 
    &lt;/span&gt;&lt;span class="n"&gt;New-HttpTestResult&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-TestArgs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$Test&lt;/span&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="nv"&gt;$TestResults&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Format-Table&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-AutoSize&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Sample Runs
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkotf179l59b6k524ybbr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkotf179l59b6k524ybbr.png" alt="Part-2 Sample Successful Output 1"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcvykf9a4ib5mv7q00jvd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcvykf9a4ib5mv7q00jvd.png" alt="Part-2 Sample Successful Output 2"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In automation scenarios, you will want to implement defensive scripts, that are not a one shot series of instructions but it has some more bells and whistles to make it more prone to environmental changes in your infrastructure. Having a retry mechanism to wait for a system's state to reach a desired state is a very common need in DevOps automation tasks. &lt;/p&gt;

&lt;p&gt;I hope in this part I was able to help you learn something new. If you are giving an attempt to the challenges, good on you. If you are just reading my solutions, do not feel guilty. I have and still reading a lot of other engineer's code on GitHub and their blogs. If you are currently not at a level to give the challenge a try yourself, download my sample, run it and try to understand how it works. Then if you can try to change something in it and see if you can change the behaviour of the script. &lt;/p&gt;

&lt;p&gt;I currently don't know what the next part will be about, I'm thinking about running our tests in a CI/CD pipeline or writing another script to create the json file with our endpoints listed automatically, via scanning an Azure subscription. Both of them can come in any order as they are not dependent on each other. I'll see you on the next one. &lt;/p&gt;

</description>
      <category>powershell</category>
      <category>devops</category>
      <category>scripting</category>
    </item>
    <item>
      <title>Practical PowerShell Scripting for DevOps - Part 1</title>
      <dc:creator>MertSenel</dc:creator>
      <pubDate>Sat, 27 Mar 2021 08:12:51 +0000</pubDate>
      <link>https://dev.to/mertsenel/practical-powershell-scripting-for-devops-part-1-1283</link>
      <guid>https://dev.to/mertsenel/practical-powershell-scripting-for-devops-part-1-1283</guid>
      <description>&lt;p&gt;Hello, friend. Welcome to Practical PowerShell Scripting for DevOps. In this series, I will attempt to teach to you how to write PowerShell scripts and solve some real-life DevOps challenges by writing scripts in PowerShell language. &lt;/p&gt;

&lt;p&gt;Scripting or Programming in general is a must have skill for anyone who want to seek a Cloud or DevOps career today. This series is geared towards giving some guidance on how to create your own PowerShell Scripts yourself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Who Is This Series For?
&lt;/h2&gt;

&lt;p&gt;DevOps engineers are tasked to operate in cloud environments with great scale of resources. Often times, scripting is an easy way to automate tasks that can be programmatically achieved to save time and produce consistent results. &lt;/p&gt;

&lt;p&gt;If you are trying to learn Powershell for getting into a Cloud / DevOps career path and struggling to find some exercise challenges to solve. &lt;/p&gt;

&lt;p&gt;This is not a best practices or formal PowerShell education resource, I merely try to provide some made up DevOps/Cloud scenarios that can be used to create some Powershell scripts in an attempt to practice and learn scripting skills.&lt;/p&gt;

&lt;p&gt;I suggest, using the Powershell reference documentation as much as possible. Spend time on terminal and play around with different cmdlets.&lt;br&gt;
Your ultimate goal should be trying to learn a style for designing and creating a automation solution for any scenario. &lt;/p&gt;

&lt;p&gt;The tools you will learn and pickup along the way while solving these scenarios will accumulate and will widen your horizon in terms of what you can do with automation and scripting. &lt;/p&gt;
&lt;h2&gt;
  
  
  Problem
&lt;/h2&gt;

&lt;p&gt;Imagine you are DevOps engineer for a project with multiple public web endpoints exposed to Internet. Your hosting provider is having an intermittent network outage on their load balancing infrastructure. You need to write a powershell script:&lt;/p&gt;

&lt;p&gt;1- That accepts a list of urls to perform Http Ping Test (Http Get Request) from a configuration file&lt;br&gt;
2- Performs a Http Get request test for given list of endpoints and return test results containing&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Status Code Returned Http Response&lt;/li&gt;
&lt;li&gt;Status Description For the Http Response&lt;/li&gt;
&lt;li&gt;Response time of the request&lt;/li&gt;
&lt;li&gt;Timestamp for the test result&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;as a collection&lt;/p&gt;

&lt;p&gt;3- Prints results to terminal in a human readable format&lt;/p&gt;
&lt;h2&gt;
  
  
  Some Pointers For Writing Your Own Solution
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Some Test Endpoints You Can Use
&lt;/h3&gt;

&lt;p&gt;Although you can use any public endpoint, I'm using &lt;a href="https://httpbin.org" rel="noopener noreferrer"&gt;https://httpbin.org&lt;/a&gt; status endpoint to randomly return me an HTTP status code for simulating a real testing scenario. When we hit a test url with multiple status codes aka Flaky Sites, we will receive different results.&lt;/p&gt;

&lt;p&gt;if you use "&lt;a href="https://httpbin.org/status/200,403,404,500" rel="noopener noreferrer"&gt;https://httpbin.org/status/200,403,404,500&lt;/a&gt;" url you will randomly get a response code back, 200 being successful and other response codes simulating different types of failures.&lt;/p&gt;

&lt;p&gt;you can use "&lt;a href="https://httpbin.org/status/200" rel="noopener noreferrer"&gt;https://httpbin.org/status/200&lt;/a&gt;" to only receive successful test results. These endpoints can be your starting point to use as a playground to curate your script.&lt;/p&gt;
&lt;h3&gt;
  
  
  The Meat Of The Solution
&lt;/h3&gt;

&lt;p&gt;Usually every script has a core functionality cmdlet you want to use, development usually starts with the results received from this core cmdlet. &lt;/p&gt;

&lt;p&gt;You can perform HTTP request in different ways in PowerShell, but most common ones are using the cmdlets:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Invoke-RestMethod&lt;/code&gt; and &lt;code&gt;Invoke-WebRequest&lt;/code&gt; &lt;/p&gt;

&lt;p&gt;I suggest starting reading documentation for these cmdlets first, try to use them to perform some tests manually in terminal first, capturing the result and then curate a plan from there. &lt;/p&gt;
&lt;h3&gt;
  
  
  Input And Output For Your Solution
&lt;/h3&gt;

&lt;p&gt;I've placed a requirement to prepare and consume the list of endpoints to tests from a file. You can use any format you like, row separated text file, a CSV file all are good. &lt;/p&gt;

&lt;p&gt;I've used and recommend JSON as it is the most richest and universal notation that can be used amongst a lot of programming languages.&lt;/p&gt;

&lt;p&gt;This skill once learnt will come in handy for you in various scenarios where you will need to handle a set of data, you can't just store and handle as a command-line argument.&lt;/p&gt;
&lt;h3&gt;
  
  
  Tasks You Need To Perform In Your Script
&lt;/h3&gt;

&lt;p&gt;1- Get your Input&lt;br&gt;
Try to figure out individual tasks you need perform first to get to a state you can actually do the work. &lt;/p&gt;

&lt;p&gt;In our case, you want to end up with your list of endpoint. To achieve this, we need to access our config file that holds our test endpoints.&lt;/p&gt;

&lt;p&gt;Maybe have a parameter for accepting the file's path and then consume the content of this file. &lt;/p&gt;

&lt;p&gt;2- Have a function to perform your Test&lt;br&gt;
It is good practice to create a function for repetitive code invocations, in our case we know that we will run test and produce a test result for multiple endpoints, hence it's a good idea to separate this part of our code to a function. &lt;/p&gt;

&lt;p&gt;3- At this point you have everything you need&lt;/p&gt;

&lt;p&gt;Loop through your test endpoints, invoke your test function for each and capture the results. &lt;/p&gt;

&lt;p&gt;4- Print the result&lt;/p&gt;

&lt;p&gt;Display the results, with way of your choosing.&lt;/p&gt;
&lt;h2&gt;
  
  
  Final Tips
&lt;/h2&gt;

&lt;p&gt;From this point below you will see my sample solution. Depending on your level and learning style, either take a look before or after you gave it your shot.&lt;/p&gt;
&lt;h2&gt;
  
  
  My Sample Solution
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Folder Structure
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqvs1f1qtbpgmzj4ge2up.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqvs1f1qtbpgmzj4ge2up.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Files
&lt;/h3&gt;
&lt;h3&gt;
  
  
  GitHub
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/MertSenel/devops-powershell/tree/main/scripts/local/practical-powershell-scripting-for-devops-part-1" rel="noopener noreferrer"&gt;Repository Url&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Tests.json&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This is the list of endpoints I would like to test, stored in JSON format which can be produced and consumed with a variety of modern languages.&lt;br&gt;
What we have here is an array of objects that has name and url keys for us to perform and label the tests.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"FlakyWebsite-#1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://httpbin.org/status/200,403,404,500"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"StableWebsite-#1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://httpbin.org/status/200"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"FlakyWebsite-#2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://httpbin.org/status/200,403,404,500"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"StableWebsite-#2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://httpbin.org/status/200"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"StableWebsite-#3"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://httpbin.org/status/200"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"StableWebsite-#4"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://httpbin.org/status/200"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"FlakyWebsite-#3"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://httpbin.org/status/200,403,404,500"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"FlakyWebsite-#4"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://httpbin.org/status/200,403,404,500"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"FaultyWebsite-#1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://httpbin.org/status/500"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our Helper Function, create this file under path &lt;code&gt;./lib/New-HttpTestResult.ps1&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This script will perform an HTTP Ping Test with passed parameters and returns a Test Result Object&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="kr"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;New-HttpTestResult&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="kr"&gt;param&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Parameter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ValueFromPipeline&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;$true&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;PSCustomObject&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nv"&gt;$TestArgs&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nv"&gt;$ProgressPreference&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'SilentlyContinue'&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="nv"&gt;$Method&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'Get'&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="nv"&gt;$duration&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Measure-Command&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nv"&gt;$Response&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Invoke-WebRequest&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Uri&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$TestArgs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;url&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Method&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$Method&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-SkipHttpErrorCheck&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;PSCustomObject&lt;/span&gt;&lt;span class="p"&gt;]@{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$TestArgs&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nx"&gt;status_code&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$Response&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;StatusCode&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ToString&lt;/span&gt;&lt;span class="err"&gt;()&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nx"&gt;status_description&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$Response&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;StatusDescription&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nx"&gt;responsetime_ms&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$duration&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Milliseconds&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nx"&gt;timestamp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;get&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;date&lt;/span&gt;&lt;span class="err"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;ToString&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'O'&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="kr"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;Test-HttpEndpoints.ps1&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Our Main script, we will invoke this script to run the tests and produce and returns a test results array&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;CmdletBinding&lt;/span&gt;&lt;span class="p"&gt;()]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="kr"&gt;param&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Parameter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ValueFromPipeline&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;$true&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nv"&gt;$TestsFilePath&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s1"&gt;'.\Tests.json'&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c"&gt;# Convert JSON Config Files String value to a PowerShell Object&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$TestsObj&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Get-Content&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$TestsFilePath&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ConvertFrom-Json&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c"&gt;# Import the Tester Function&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;/lib/New-HttpTestResult.ps1&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c"&gt;# Loop through Test Objects and get the results as a collection&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$TestResults&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;foreach&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$Test&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$TestsObj&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; 
    &lt;/span&gt;&lt;span class="n"&gt;New-HttpTestResult&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-TestArgs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$Test&lt;/span&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="nv"&gt;$TestResults&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Format-Table&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-AutoSize&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Sample Runs
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhtwlhr8v79uv2bwcav0z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhtwlhr8v79uv2bwcav0z.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On each run, there is chance to get a different result back for flaky test endpoints. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Femqghft54ayj1sv4bxmy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Femqghft54ayj1sv4bxmy.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;I hope this sample challenge helped you out learning powershell a bit and give it more context on how you can craft custom tools, in a short time frame.&lt;/p&gt;

&lt;p&gt;You may share your solutions, via the comments if you wish me to review them or have any questions.&lt;/p&gt;

&lt;p&gt;In Part 2 of this series, I am planning to go into Azure Cloud and I will share another challenge with usage of Az PowerShell module. &lt;br&gt;
Hope to see you there. &lt;/p&gt;

</description>
      <category>powershell</category>
      <category>devops</category>
      <category>scripting</category>
    </item>
    <item>
      <title>Agile Cloud DevOps Engineer</title>
      <dc:creator>MertSenel</dc:creator>
      <pubDate>Sun, 03 Jan 2021 11:18:49 +0000</pubDate>
      <link>https://dev.to/mertsenel/agile-cloud-devops-engineer-51n4</link>
      <guid>https://dev.to/mertsenel/agile-cloud-devops-engineer-51n4</guid>
      <description>&lt;p&gt;Designs like a Cloud Architect, Ships like a Developer, Operates like a Site Reliability Engineer My transformation in 2020, and what Agile/Embedded DevOps Engineer Model is all about&lt;/p&gt;




&lt;p&gt;2020 is finally over and it has been a hell of ride for me and I guess for everybody around the globe. Although, it was not the best year in my life personally as I have suffered a great length of social isolation due to COVID and some unfortunate circumstances, I still manage to hold myself together, be productive and kept growing. I may not be at my happiest state, but I certainly didn't made this an excuse to stop myself from working extra hard to pursue my goals and dreams, while keeping up with my professional duties. I feel more resilient and confident as a result of all of it. Besides all the negative, career wise this year also felt like the most accomplished year for me.  &lt;/p&gt;

&lt;p&gt;I will try to keep this article focused on my transformation in 2020, and try to explain you what Agile/Embedded DevOps Engineer model/role is.&lt;/p&gt;

&lt;h2&gt;
  
  
  2020 Highlights
&lt;/h2&gt;

&lt;p&gt;First, a short list of my major achievements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;New role as an Agile/Embedded DevOps Engineer&lt;/li&gt;
&lt;li&gt;A great deal of improvement my technical skills in PowerShell, Azure Cloud, Azure DevOps, Docker, Infrastructure as Code, Automation, Monitoring, CI/CD and Automation.&lt;/li&gt;
&lt;li&gt;Opened my first personal blog/portfolio website.&lt;/li&gt;
&lt;li&gt;My first open source contribution to Azure quickstart repository.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fnh4q2uh3a0f405ztch8m.png" alt="Alt Text"&gt;
&lt;/li&gt;
&lt;li&gt;Published 6 &lt;a href="https://mertsenel.tech/post/" rel="noopener noreferrer"&gt;posts&lt;/a&gt; and 6 DevOps &lt;a href="https://mertsenel.tech/#projects" rel="noopener noreferrer"&gt;projects&lt;/a&gt; in my website.
&lt;/li&gt;
&lt;li&gt;My &lt;a href="https://mertsenel.tech/project/azure-multi-region-scalable-web-architecture/" rel="noopener noreferrer"&gt;submission&lt;/a&gt; won the Azure Festive Tech Calendar Automation Deployment Hackathon&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fljld5g27jnbcaqz8xw3d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fljld5g27jnbcaqz8xw3d.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.wesleyhaakman.org/festive-tech-hackathon/" rel="noopener noreferrer"&gt;https://www.wesleyhaakman.org/festive-tech-hackathon/&lt;/a&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I had connected and engaged with a lot of Cloud/DevOps/SRE professionals and content creators of all kind around the world via social media, learnt from what they are sharing and try to share my knowledge wherever I can to pay forward what I have been receiving online.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  So What is Agile/Embedded DevOps Engineer
&lt;/h2&gt;

&lt;p&gt;First of all, the exact terminology that we actually use in my company is "Embedded DevOps Engineer", Agile is something I've come up with so it resonates with a larger audience. If you have never heard this concept, don't worry I had not too until December 2019.&lt;/p&gt;

&lt;p&gt;It all happened during my initial meeting with my current company's Vice President of Operations. He asked me, what do you think your role will be here like? I told him that I would oversee all DevOps related aspects of the project and pretty much drive the project, take it to the finish line.  &lt;/p&gt;

&lt;p&gt;As an operations engineer this is usually what is expected of you, being the owner of a particular project or a solution and delivering it end to end. Having "Root” level access to all, being keeper of secrets and systems.  &lt;/p&gt;

&lt;p&gt;His answer was short, he simply denied this approach and started to explain to me they are going to be implementing a new model called "Embedded DevOps”  where an "Embedded DevOps” engineer(s) is a directly assigned resource of a software delivery team. Moreover, the DevOps mentality will be expected and adopted by all members of the engineering teams. So it will be a shared duty amongst everybody to operate the services.&lt;/p&gt;

&lt;p&gt;The embedded engineers will also be engaged in a central "DevOps Practice” team that they would allocate a certain amount of time per week to work on common goals and practices that should be promoted to the rest of the engineering teams.&lt;/p&gt;

&lt;p&gt;Then he asked me, what do you think about it? I said great! Let’s do it. Fast forward 12 months and it was surely one of the best work experiences I had so far.  &lt;/p&gt;

&lt;h2&gt;
  
  
  Making the best use of your DevOps Talent
&lt;/h2&gt;

&lt;p&gt;Having a group of talented DevOps engineers in your company is great, but they are no good to anyone if they are isolated from the application development processes. If they are kept aside in yet another silo labeled as “DevOps”. Instead having them embedded in the software development teams, would make their knowledge, skills and their unique perspective available to the software project, from day one.&lt;br&gt;&lt;br&gt;
Increasing the engagement also allows for software and QA engineers to learn DevOps concepts. Likewise, as a DevOps engineer I feel like I have learnt a lot in terms of software development too.  &lt;/p&gt;

&lt;p&gt;This is possible because there is almost no resistance in terms of accepting change. This is very important, that is why as DevOps engineers we shouldn't  have to drive the change and DevOps adoption. Instead it should be everyone’s desire inside an organization to implement and practice DevOps while developing, delivering and supporting their products. Hence, a DevOps engineer's duty is more on enabling and improving these practices. Providing solutions to problems, where engineering teams need assistance.&lt;/p&gt;

&lt;h2&gt;
  
  
  What about company wide DevOps Challenges
&lt;/h2&gt;

&lt;p&gt;This is where DevOps practice comes into play. Having embedded DevOps engineers are great, but this fills their day with development and operations tasks for their assigned product(s).  &lt;/p&gt;

&lt;p&gt;All DevOps engineers embedded or within the central DevOps team, all of them join once a week for a catch-up type of meeting where the structure is fluid. It may include, planning, announcements, discussion points and so on. Then, DevOps engineers in groups of four or five, split to work on different larger scope projects.&lt;/p&gt;

&lt;p&gt;These projects are problems that are concerning the whole company. These are problems where a larger collaboration is needed either for designing or implementing a solution to a particular problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Requirements of the Agile/Embedded DevOps Model
&lt;/h2&gt;

&lt;p&gt;Below are my key highlights of the necessary practices that needs be in place for a DevOps practice that works in this fashion:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;DevOps from the beginning, designing &lt;a href="https://12factor.net/" rel="noopener noreferrer"&gt;12 factor apps&lt;/a&gt; means our projects are suited for faster change delivery. We are able to deliver a change in a matter of hours if needed.  &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Security starts from code; with strong collaboration between security, software and DevOps engineers, source code, infrastructure and our application architecture is secured and hardened according to industry best practice starting from the development phase.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Proactive and pre-planned work; this model allows for better allocation of DevOps engineer’s time in terms of task planning. They are clear of their responsibilities and priorities. This allows for better planning for their managers and themselves. This is a much better allocation compared to reactive and random project assignments that leaves DevOps engineers overwhelmed and alienated.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Collaboration and transparency is encouraged across the company. Inside the company, information is free to flow, so everyone can learn. Repositories, Wikis, document sites are there for us to work better and find solutions faster.  &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Sharing meetings, demos and similar workshops are regularly held.  &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;On-demand training resources are easily available and bootcamp type more structured training is offered on a regular schedule.  &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Using Infrastructure as Code and end to end Automated Continuous Delivery.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Design Like an Architect - Your Personal Cloud SME
&lt;/h2&gt;

&lt;p&gt;One my of duties is, acting as an immediately reachable Cloud SME(Subject Matter Expert), to my team and managers. Being a cloud enthusiast, this is one of the areas I excel at, and I have great pleasure when I have to work with this nature of a task or a story.  &lt;/p&gt;

&lt;p&gt;This year, my role allowed me to curate and extend cloud infrastructure for both brownfield and greenfield services either we host as a part of our customer facing products, or a line of business service we are hosting internally, that we use for business intelligence or software development operations.  &lt;/p&gt;

&lt;p&gt;Taking part in these major decisions is very important as the decisions made at this stage can make or break your solution's:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Usability&lt;/li&gt;
&lt;li&gt;Reliability&lt;/li&gt;
&lt;li&gt;Scalability&lt;/li&gt;
&lt;li&gt;Availability&lt;/li&gt;
&lt;li&gt;Testability&lt;/li&gt;
&lt;li&gt;Supportability&lt;/li&gt;
&lt;li&gt;Security&lt;/li&gt;
&lt;li&gt;Liability&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Which are common features for almost all enterprise software we use day to day. No matter how good a software product is, it won't be successful without having the features listed above. If you have frequent outages, or you can't figure out your products scaling bottleneck which ends up in poor performance, it probably won't attract a lot of market value. These are now expected as a default baseline by software customers and DevOps really shines assisting in delivery of these features.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ship Like a Developer - Scrum for Ops
&lt;/h2&gt;

&lt;p&gt;One of the main differences in this working style is that, my work is also planned in 2 week scrum sprints. This forced me into breaking down my stories and tasks, have a plan and a list of deliverables, acceptance criteria in a well documented manner that works for us. Effort estimations were difficult at first but, the more you do it the easier it gets.&lt;/p&gt;

&lt;p&gt;My work is reviewed and merged via Pull Requests and rest of the team is familiarized and kept up to date of the process during Scrum meetings.Working closely with the Application Developers, UI/UX and Product Management allowed us to have a much more efficient SDLC processes overall that boosted up everybody's effectiveness.&lt;/p&gt;

&lt;p&gt;It's not my character to contain myself inside a box, so I usually overreached and gone beyond my duties in understanding business logic of the services I worked on, which in return presented itself as enhanced value to the team and the products we are shipping.&lt;/p&gt;

&lt;p&gt;Another significant benefit of working inside a development team is, knowledge sharing. I have certainly improved my programming skills, Database/Query language skills, CI/CD skills for different technology and infrastructure stacks and overall my comfort level while bouncing ideas of other Developers. I'm also sure that on the other hand my teammates after working with me now have a better understanding in cloud infrastructure, networking and overall software operations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Operate Like a SRE (Site Reliability Engineer) - Modern SysAdmin in NoOps Era
&lt;/h2&gt;

&lt;p&gt;Coding and releasing infrastructure is great and fun, but the story doesn't usually ends there. A developer too can most likely put together some infrastructure as code or automation to bring up infrastructure for their projects, but performing as a SRE requires a unique set of skills and mindset which cannot be overlooked.&lt;/p&gt;

&lt;p&gt;While developing an application, in conjunction we are also developing code/config that provides integration with monitoring and observability tooling for alerts and visibility, configuration management, secret management, release management and more. &lt;/p&gt;

&lt;p&gt;Deployment patterns like blue-green deployments for stateless services, schema updates to your database layers these all require some unique expertise to be planned, automated and performed.&lt;/p&gt;

&lt;p&gt;I use to be a system administrator that does the same for infrastructure of our own, but as more and more compute is being passed on the cloud service provider's "SysAdmins" we get to operate our "Services" instead of "Infrastructure". &lt;/p&gt;

&lt;p&gt;So, we won't be concerning ourselves, for hosting a highly available web server or a database server, PaaS offerings already gives us much of these, but instead the overall harmony of our micro services, their integration, the SLA's and SKUs we require to pay in order to fulfil our solution's requirement. This does not mean we can't, if need rises we can but we simply choose not to whenever possible as it all adds up as toils.&lt;/p&gt;

&lt;p&gt;We also don't concern ourselves by hosting our CI/CD tooling, source control or any other collaboration tool and opt-in for cloud native SaaS and PaaS offerings wherever possible. Anything that adds an admin effort is actually more costly to a business. Your customers don't care about how well you host your Jenkins clusters or Octopus Deploy server, all the value is in the delivered software so the focus should be on those tasks, not things where there is someone else doing it as a commercial product.&lt;/p&gt;

&lt;p&gt;All of this is also coherent with the designing element of this role. You can think about the whole picture, including the operations duties while making the architecture choices for your application/infrastructure. &lt;/p&gt;

&lt;p&gt;Knowing a service by it's nature is very important in it's uptime, health and reliability. For a messaging infrastructure like a Kafka server or a object storage service like Azure Storage Accounts, you may be only needing do a metrics based monitoring but what about an API that is sitting behind a WAF and requires OAuth authentication, you cannot simply ping your API, you need to know your service in order to monitor it's health state and performance. This is were SRE skills comes into play that incorporates all of the supporting services, side by side with the actual application/service.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Overall, I can say that this year was another year of great deal of learning, accomplishment, recognition and action taking. I'm very proud of myself to go through this transformation and came out fine on the other side. I may be wronged in the future but I believe, our limits are defined by the mental ceilings we put for ourselves. I always actively try to set a higher ceiling and standards for myself. The only way to reach those ceilings is believing in myself that I will get there and put in the effort necessary, no excuses and by all means. I admit that I'm my own biggest fan and I suggest you should be yours too. If you don't believe in yourself, how can you expect others to do so.  &lt;/p&gt;

</description>
      <category>devops</category>
      <category>cloud</category>
      <category>career</category>
      <category>agile</category>
    </item>
    <item>
      <title>Microservices Lab on Azure Kubernetes Service</title>
      <dc:creator>MertSenel</dc:creator>
      <pubDate>Sun, 04 Oct 2020 08:56:28 +0000</pubDate>
      <link>https://dev.to/mertsenel/microservices-lab-on-azure-kubernetes-service-5411</link>
      <guid>https://dev.to/mertsenel/microservices-lab-on-azure-kubernetes-service-5411</guid>
      <description>&lt;h1&gt;
  
  
  Microservices on cloud-based Kubernetes - Azure
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Ne5e8gII--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://github.com/MertSenel/microservices-on-cloud-kubernetes-azure/workflows/Microservices%2520on%2520AKS%2520E2E%2520CI/badge.svg%3Fbranch%3Dmain" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Ne5e8gII--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://github.com/MertSenel/microservices-on-cloud-kubernetes-azure/workflows/Microservices%2520on%2520AKS%2520E2E%2520CI/badge.svg%3Fbranch%3Dmain" alt="Microservices on AKS E2E CI"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://opensource.org/licenses/Apache-2.0"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--buXHYjul--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://img.shields.io/badge/License-Apache%25202.0-blue.svg" alt="License"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Introduction
&lt;/h3&gt;

&lt;p&gt;Inspired by the Original Project: &lt;a href="https://github.com/didier-durand/microservices-on-cloud-kubernetes"&gt;https://github.com/didier-durand/microservices-on-cloud-kubernetes&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is an implementation of the same micro-services pattern deployment lab implemented with Azure Kubernetes Services. &lt;/p&gt;

&lt;p&gt;(see Credits Section for more information).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The purpose of this repository is to provide the fully automated setup of a nice-looking (see &lt;a href="https://github.com/GoogleCloudPlatform/microservices-demo#screenshots"&gt;screenshots&lt;/a&gt;) &lt;br&gt;
showcase / testbed for a cloud-native (&lt;a href="https://docs.microsoft.com/en-us/dotnet/architecture/cloud-native/definition"&gt;precisely defined&lt;/a&gt; application&lt;br&gt;
by Microsoft) on a cloud-hosted Kubernetes cluster (here &lt;a href="https://cloud.google.com/kubernetes-engine"&gt;GKE by Google Cloud&lt;/a&gt;) based on an interesting &lt;a href="https://www.redhat.com/en/topics/microservices/what-is-a-service-mesh"&gt;service mesh&lt;/a&gt;. &lt;br&gt;
So, additionally, the setup will install tooling (coming from the &lt;a href="https://github.com/cncf/trailmap"&gt;CNCF Cloud Trail Map&lt;/a&gt; for many of them) to make the application and its service mesh observable and manageable.&lt;/p&gt;

&lt;p&gt;Another goal of this repository is to help people exploring the cloud-native architecture: when you fork it, you rapidly get a working cluster with a somewhat &lt;br&gt;
"real-life" application and decent tooling to experiment with, without the need for a long trial-and-error process starting with infrastructure to set it &lt;br&gt;
up from scratch. It makes it much faster to grasp the philosophy of the distributed architecture proposed by Kubernetes.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  My Purpose for the AKS / Cloud Kubernetes Lab
&lt;/h4&gt;

&lt;p&gt;My purpose was to create a Cloud Native Kubernetes Lab, for Operations, DevOps and Site Reliability Engineering Perspective.&lt;/p&gt;

&lt;p&gt;One of the main problem for training complex infrastructure scenarios is that, it's not always possible to have an application workload to work with that can simulate real life scenarios, or close to a production workload in terms of complexity. &lt;/p&gt;

&lt;p&gt;My purpose with this repository is to have a baseline for quick and fast start with a function Azure Kubernetes Lab. &lt;/p&gt;

&lt;p&gt;Then use the infrastructure to work on more advanced DevOps concepts. &lt;/p&gt;

&lt;p&gt;Some improvements I can think of right now:&lt;br&gt;&lt;br&gt;
1- Learn and Practice with the Observability and Monitoring Tools&lt;br&gt;&lt;br&gt;
2- Practice IaC and Pipeline Concepts, (GitHub Action provided here is not a good reference for proper CI/CD, they are quick and easy methods to keep everything  contained to a GitHub Repository)&lt;br&gt;
3- Practice Kubernetes Cluster Administration Operations, Upgrade Cluster Version, Scale-up, Scale-Out&lt;br&gt;&lt;br&gt;
4- Setup Advanced Networking for Ingress. Currently project used unprotected HTTP protocol, having the traffic secured via SSL can be an improvement.&lt;br&gt;&lt;br&gt;
5- Practice, more advanced load testing, stress testing and reliability testing (chaos monkey).&lt;br&gt;&lt;br&gt;
6- Learn how to co-host multiple team's operations on a single AKS cluster (developing different services) using AKS Dev Spaces and Kubernetes namespaces in general.&lt;br&gt;
7- Build the application services from source, and customize them to integrate with Azure Application Insights for APM and Telemetry Logging.  &lt;/p&gt;

&lt;h2&gt;
  
  
  Access to deployed tools &amp;amp; dashboards
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;Available dashboards:&lt;/strong&gt;&lt;/em&gt;_&lt;/p&gt;

&lt;p&gt;(click on pictures to enlarge them - also use the hyperlinks provided with each dashboard description to have a good overview of the &lt;br&gt;
features of each tool from its official documentation&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;del&gt;&lt;strong&gt;Standard K8s UI&lt;/strong&gt;: our workflow deploys first &lt;a href="https://kubernetes.io/docs/tasks/access-application-cluster/web-ui-dashboard/"&gt;this standard Kubernetes dashboard&lt;/a&gt; 
as a tool that should anyway be included in any installation. It gives a good overview of the deployed cluster with static (configuration) and dynamic (metrics) information 
about the active objects. When &lt;em&gt;&lt;code&gt;kubectl proxy&lt;/code&gt;&lt;/em&gt; is active, the dashboard is available at &lt;a href="http://localhost:8001/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/"&gt;this url&lt;/a&gt;. 
The security check at login is most easily satisfied by selection the login option of config file (see Prereqs to obtain it in Setup section).&lt;/del&gt; &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gH2_vmLB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/knttys6s1hzy4ouqruqq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gH2_vmLB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/knttys6s1hzy4ouqruqq.png" alt="aks-workloads"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ERahH2uv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/2m2swvxxok6joeykwq0x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ERahH2uv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/2m2swvxxok6joeykwq0x.png" alt="aks-portal-services"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--h25Kor7L--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/6dk9sa9xbpf6tbwfu2gg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--h25Kor7L--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/6dk9sa9xbpf6tbwfu2gg.png" alt="azure-container-monitoring"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--NaTnV5j3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/al2qwnfxmtzua2dgt0j2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--NaTnV5j3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/al2qwnfxmtzua2dgt0j2.png" alt="azure-container-monitoring-2"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Azure Portal &amp;amp; Azure Container Insights Solution&lt;/strong&gt;
As per Azure &lt;a href="https://docs.microsoft.com/en-us/azure/aks/kubernetes-dashboard"&gt;Documentation&lt;/a&gt; Kubernetes Dashboard is soon to be deprecated and recommended solution is to use Azure Container Insights Solution which brings, the management plane visibility to cloud providers operations tools.
This gives a better controls(in terms of access and security) and operations capability to the cluster operators.
My implementation, deploy Azure Container Insights Monitoring Solution with the ARM template. ARM templates, is considered Infrasructure as Code and includes can include it's own workflow logic.
Hence, the template deployment flow looks like below:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;1- Deploys a Log Analytics Workspace&lt;/p&gt;

&lt;p&gt;2- Deploy the Azure Kubernetes Cluster, with Monitoring Add-On enabled.&lt;/p&gt;

&lt;p&gt;3- Deploys the Container Insights Management Solution to Log Analytics Workspace, created in step1. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--rm8i2vbL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/l5j1upbv66gri7xcvjsd.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--rm8i2vbL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/l5j1upbv66gri7xcvjsd.jpg" alt="polaris-dashboard"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Polaris dashboard&lt;/strong&gt;: &lt;a href="https://github.com/FairwindsOps/polaris"&gt;Polaris&lt;/a&gt; is an interesting tool, even in its OSS version (the paid-for version 
provides more checks) used here. After installation, it &lt;a href="https://www.fairwinds.com/polaris"&gt;scans the definitions&lt;/a&gt; of various kinds of objects and applies sanity checking rules to 
validate their proper configuration. For example, in the case of Online Boutique, it will warn that containers have no resource constraints (cpu, 
memory, etc.) imposed on them or that their security credentials are too wide compared to what they do. The hyperlinks provided on the unsatisfactory 
checks document the reason(s) of the alert as well as the possible remedies to apply. So, a quite useful tool to incrementally increase the quality of the 
configuration of a given cluster: new versions of yaml object manifests can be gradually deployed to minimize the issue notifications.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8EaU5RY7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/omoknryevh202aljam70.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8EaU5RY7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/omoknryevh202aljam70.jpg" alt="kiali-dashboard"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Kiali Dashboard&lt;/strong&gt;: &lt;a href="https://kiali.io/"&gt;Kiali&lt;/a&gt; claims itself the &lt;em&gt;"Service mesh management for Istio"&lt;/em&gt;. it allows an interactive discovery of 
the defined relations between the services. Then, it provides &lt;a href="https://kiali.io/documentation/latest/features/"&gt;detailed insights and metrics&lt;/a&gt; about the 
health of those services and the requests between them.  The live traffic animation in the UI is extremely useful: it allows to spot very quickly 
where the activity happens to focus on those hot spots during root cause analysis for an issue. You can also go back in time with the replay 
feature to see the traffic and the interactions that happened in the past to understand why and how you reached current situation. This dashboard 
is accessed via &lt;em&gt;&lt;code&gt;istioctl dashboard kiali&lt;/code&gt;&lt;/em&gt; that will open the corresponding UI into your web browser.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--WOEHG9_6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/ibq4n6osmy3xwwvkv1s7.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WOEHG9_6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/ibq4n6osmy3xwwvkv1s7.jpg" alt="grafana-dashboard"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Grafana dashboard&lt;/strong&gt;: &lt;a href="https://grafana.com/oss/grafana/"&gt;Grafana&lt;/a&gt; which &lt;em&gt;&lt;code&gt;allows you to query, visualize, alert on metrics and logs, no 
matter where they are stored&lt;/code&gt;&lt;/em&gt; provides very nice charts about the activity of the cluster as the whole (usual metrics about resource consumption: 
cpu, memory, etc. for nodes. But, more specifically in this context it provides interesting additional &lt;a href="https://istio.io/latest/docs/tasks/observability/metrics/using-istio-dashboard/"&gt;dashboards specific to the Istio service mesh&lt;/a&gt; 
related to the traffic between the pods. Those dashboards are accessed via &lt;em&gt;&lt;code&gt;istioctl dashboard grafana&lt;/code&gt;&lt;/em&gt; that will open the corresponding UI into 
your web browser.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--JCQVQQQt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/epwjl8mgdegbxwzoohgk.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--JCQVQQQt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/epwjl8mgdegbxwzoohgk.jpg" alt="jaeger-dashboard"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Jaeger dashboard&lt;/strong&gt;: the Open Boutique is instrumented via &lt;a href="https://opencensus.io/"&gt;OpenCensus&lt;/a&gt;, now merged into 
&lt;a href="https://opentelemetry.io/"&gt;OpenTelemetry&lt;/a&gt;, component of the CNCF Cloud Trail Map. Jaeger - in CNCF Cloud Trail Map - is the tracing backend 
implemented here. It centralizes and ingests the distributed traces produced by the various microservices. So, the Jaeger dashboard will allow the 
detailed examination of those aggregated distributed traces also known as &lt;a href="https://opentracing.io/docs/overview/spans/#what-is-a-span"&gt;"spans"&lt;/a&gt;. This 
dashboard is accessed via &lt;em&gt;&lt;code&gt;istioctl dashboard jaeger&lt;/code&gt;&lt;/em&gt; that will open the corresponding UI into your web browser.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--UIfPc_tf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/40tkwdyhbgykb3m4hi0p.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--UIfPc_tf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/40tkwdyhbgykb3m4hi0p.jpg" alt="prometheus-dashboard"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Prometheus dashboard&lt;/strong&gt;: &lt;a href="https://prometheus.io/"&gt;Prometheus&lt;/a&gt; (also member of CNCF Cloud Trail Map) is the cornerstone component for metrics 
collection. The collected data is used by Grafana, Kiali &amp;amp; Jaeger for their specific purposes. The Prometheus dashboard can be used as the "source 
of truth": for example, it can be used to verify if some metrics claimed as missing by a downstream component using this value is really collected 
or not and to compare the graph produced by Prometheus itself to the graph produced downstream user to spot potential discrepancies. This dashboard 
is accessed via &lt;em&gt;&lt;code&gt;istioctl dashboard prometheus&lt;/code&gt;&lt;/em&gt; that will open the corresponding UI into your web browser.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--SX0R3ONY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/4fo4rz43oze6fekz8vlz.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SX0R3ONY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/4fo4rz43oze6fekz8vlz.jpg" alt="envoy-dashboard"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Envoy dashboard&lt;/strong&gt;: an improved version of &lt;a href="https://www.envoyproxy.io/docs/envoy/latest/intro/what_is_envoy"&gt;Envoy Proxy&lt;/a&gt;, also part of the CNCF Cloud Trail Map,  is the &lt;a href="https://istio.io/latest/docs/concepts/what-is-istio/#why-use-istio"&gt;sidecar
container&lt;/a&gt; used by Istio. Envoy provides a (somewhat rough) dashboard to go into the 
nitty-gritty details of a given pod: it is usually used for low-level introspection into the traffic between pods in unexpected situations. It is 
more a debugging tool at very low-level: most cluster administrators shouldn't need to use it. This dashboard is accessed via &lt;em&gt;&lt;code&gt;istioctl 
dashboard envoy podname[.namespace]&lt;/code&gt;&lt;/em&gt;: it will open the corresponding UI for the chosen pod sidecar into your web browser.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Workflow steps
&lt;/h2&gt;

&lt;p&gt;The workflow has following steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;checkout of project artefacts&lt;/li&gt;
&lt;li&gt;Install Istioctl to the agent&lt;/li&gt;
&lt;li&gt;Login to Azure Context and Install Tools Az Cli and Az Pwsh(subsequent steps in shell script)&lt;/li&gt;
&lt;li&gt;Deploy Infrastructure, which includes the Azure side Monitoring Infra and Integration&lt;/li&gt;
&lt;li&gt;import cluster config &amp;amp; credentials for kubectl &amp;amp; istioctl&lt;/li&gt;
&lt;li&gt;deploy Polaris dashboard and check its proper deployment&lt;/li&gt;
&lt;li&gt;deploy Istio and check its proper deployment&lt;/li&gt;
&lt;li&gt;deploy Istio-based addons and check their proper deployment&lt;/li&gt;
&lt;li&gt;label application namespace to ensure automatic sidecar injection (see &lt;a href="https://istio.io/latest/docs/ops/deployment/architecture/"&gt;Istio architecture&lt;/a&gt; uses an improved version of Envoy for sidecars) in microservice pods&lt;/li&gt;
&lt;li&gt;
&lt;del&gt;deploy proper application manifests for Istio&lt;/del&gt; &lt;strong&gt;Not need for Azure&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;deploy Online Boutique and check its proper deployment&lt;/li&gt;
&lt;li&gt;run kubectl get all to display all deployed assets and IP address information&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Application can now be accessed as described below&lt;/p&gt;

&lt;h3&gt;
  
  
  AKS Lab Demo User Snippets
&lt;/h3&gt;

&lt;p&gt;You can start using your lab via instructions below.&lt;/p&gt;

&lt;h4&gt;
  
  
  Setup Project Resource Names
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="c"&gt;#Curate Variables&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$ResourceGroupName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mert-lab-aks-aue01"&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c"&gt;#Update with your RG Name&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$AksClusterName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mertlabaue01-aks"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;#Update with your AKS Lab Cluster Name&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Get your Kubectl Credentials
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;Import-AzAksCredential&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-ResourceGroupName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$ResourceGroupName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$AksClusterName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Force&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Get All Objects in Kubernetes Cluster
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;kubectl get all
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Find the IP Address of the Online Boutique FrontEnd
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$FrontEndService&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;kubectl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;get&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;service/frontend-external&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-o&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ConvertFrom-Json&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Depth&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;100&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$FrontEndServiceUri&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http://{0}"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$FrontEndService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loadBalancer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ingress&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ip&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="n"&gt;Write-Output&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Online Boutique FrontEnd Uri: &lt;/span&gt;&lt;span class="nv"&gt;$FrontEndServiceUri&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;Online Boutique FrontEnd Uri: http://52.147.9.72
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Get Logs from Load Generator
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;kubectl logs -l app=loadgenerator -c main
 GET /product/66VCHSJNUP                                          600     0(0.00%)      77      34    1048  |      41    0.10    0.00
 GET /product/6E92ZMYYFZ                                          563     0(0.00%)      77      34    1763  |      41    0.00    0.00
 GET /product/9SIQT8TOJO                                          593     0(0.00%)      73      34    1013  |      41    0.30    0.00
 GET /product/L9ECAV7KIM                                          631     0(0.00%)      82      34    1349  |      42    0.20    0.00
 GET /product/LS4PSXUNUM                                          608     0(0.00%)      83      34     896  |      42    0.20    0.00
 GET /product/OLJCESPC7Z                                          623     0(0.00%)      69      34    1079  |      41    0.10    0.00
 POST /setCurrency                                                808     0(0.00%)      82      44    1089  |      51    0.20    0.00
--------------------------------------------------------------------------------------------------------------------------------------------
 Aggregated                                                      9517     0(0.00%)                                       1.80    0.00
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Credits
&lt;/h3&gt;

&lt;p&gt;Inspired by the Original Project: &lt;a href="https://github.com/didier-durand/microservices-on-cloud-kubernetes"&gt;https://github.com/didier-durand/microservices-on-cloud-kubernetes&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is an implementation of the same micro-services pattern deployment lab implemented with Azure Kubernetes Services. &lt;/p&gt;

&lt;p&gt;To read his full README and instructions click &lt;a href="https://github.com/didier-durand/microservices-on-cloud-kubernetes"&gt;here&lt;/a&gt;. I will be moving relevant parts to this repo and update with new instructions, specific to our implementation.&lt;/p&gt;

&lt;h3&gt;
  
  
  License
&lt;/h3&gt;

&lt;p&gt;Below is &lt;a href="https://github.com/didier-durand"&gt;Didier&lt;/a&gt;'s License notice, and I'm carrying over the same LICENSE, so you are also free to re-use the resources in the repository.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This application, licensed under Apache terms (same terms for all components used in this worklfow - So, allowing free reuse) is the &lt;a href="https://github.com/GoogleCloudPlatform/microservices-demo"&gt;"Online Boutique"&lt;/a&gt;&lt;br&gt;
 (formerly known as Hipster Shop - developed by a Google team but not an official product of them). It is composed of 10 polyglot microservices behind a nice-looking web frontend calling them to serve client requests. &lt;br&gt;
 A load-generator - part of the package - will generate traffic while the application is running to make use of tools (Prometheus, OpenTelemetry, &lt;br&gt;
 etc.) more attractive.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Application features &amp;amp; service mesh
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--PwKrEAmd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/ps5olmk55rrdm6rhtf36.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PwKrEAmd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/ps5olmk55rrdm6rhtf36.png" alt="online-boutique-architecture"&gt;&lt;/a&gt;&lt;/p&gt;
application service mesh



&lt;p&gt;This demo application contains an interesting service mesh to give some substance to demos and tests: its schema is given above. This mesh &lt;br&gt;
which is thoroughly used by a traffic generator - also part of the demo package - which generates constant solid traffic to make the implementation &lt;br&gt;
of monitoring tools.&lt;/p&gt;

&lt;p&gt;Interesting points of Online Boutique:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Multi-language:&lt;/strong&gt; the microservices corresponding to the various application features were on purpose written on purpose by the authors in &lt;br&gt;
numerous languages (Go, NodeJS, Java, C#, Python) to demonstrate a key strength of container-based applications: many microservices collaborate in &lt;br&gt;
a "polyglot" environment where each team (or individual) can program in its language of choice while ad hoc frameworks for each language make sure &lt;br&gt;
that all Kubernetes standards (probes, etc.) and architecture can be easily respected with minimal effort to obtain a coherent and compliant global &lt;br&gt;
system, manageable by the standard palette of Kubernetes tools. This polyglot aspect is reinforced by the mixed use of http and gRpc, which are both&lt;br&gt;
understood by the monitoring tools.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Service Mesh:&lt;/strong&gt; the application graph shows relationships between the various services and the front-end. Indeed, the application &lt;br&gt;
is made of13 pods. This high-level of granularity is the accepted Kubernetes pattern for application architecture: it brings numerous advantages like continuous &lt;br&gt;
delivery, exhaustive unit testing, higher resilience, optimal scalability, etc. But, it also requires the use of a thorough set of tools to &lt;br&gt;
maximize the observability of the system. If "divide and conquer" is the motto of cloud-native architectures, the motto of their operations is probably &lt;br&gt;
"observe to sustain": when working with Kubernetes application, one feels very quickly the need for (very) solid tools monitoring automatically &lt;br&gt;
the myriad of objects (services, pods, ingress, volumes, etc.) composing the system.  &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;GCP Tooling:&lt;/strong&gt; the application is instrumented for &lt;a href="https://en.wikipedia.org/wiki/Stackdriver"&gt;Stackdriver (profiling, logging, debugging)&lt;/a&gt;. So, &lt;br&gt;
the source code of this application provides the right guidance to see how to code in order to obtain the right leverage on tools directly available &lt;br&gt;
from the GCP service portfolio.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Setup for forks
&lt;/h3&gt;

&lt;p&gt;To start with, you need an active Azure account that you can use the providers required for this project, like Kubernetes and Azure Monitor.&lt;/p&gt;

&lt;p&gt;Then, fork our repository and define the required &lt;a href="https://docs.github.com/en/actions/reference/encrypted-secrets"&gt;Github Secrets&lt;/a&gt; in your forked &lt;br&gt;
repository:&lt;/p&gt;

&lt;p&gt;All you need for this project is to setup one secret called &lt;strong&gt;"AZURE_CREDENTIALS"&lt;/strong&gt;. &lt;br&gt;
Open the run the powershell script &lt;strong&gt;'New-AzAksLabPrerequisites.ps1'&lt;/strong&gt; you can find &lt;a href="///ps1/tools/New-AzAksLabPrerequisites.ps1"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This script will create a Service principal, save the credential json file to your local as &lt;strong&gt;"AZURE_CREDENTIALS.json"&lt;/strong&gt; and also create a new environment variable called &lt;strong&gt;"AZURE_CREDENTIALS"&lt;/strong&gt; in your local system. &lt;/p&gt;

&lt;p&gt;This local environment variable part is optional, but it makes it easier for you to also run the scripts locally, without using github actions. &lt;/p&gt;

&lt;p&gt;Once you have the json file generated, copy and paste the contents of that file as your &lt;strong&gt;"AZURE_CREDENTIALS"&lt;/strong&gt; Github Repository Secret.&lt;/p&gt;

&lt;p&gt;To easily launch the workflow, you can launch it with the &lt;a href="https://github.blog/changelog/2020-07-06-github-actions-manual-triggers-with-workflow_dispatch/"&gt;manual dispatch feature of Github&lt;/a&gt; that you can see as a launch button in the Action tab of your project for &lt;br&gt;
the &lt;strong&gt;"Deploy AKS Lab Infra and Services"&lt;/strong&gt; workflow. Similarly, you can stop it via similar button in &lt;strong&gt;"Clean-Up AKS Lab Resources"&lt;/strong&gt; workflow.&lt;/p&gt;

&lt;p&gt;If you already have deployed the Infrastructure but something went wrong during services installation you can run &lt;strong&gt;"Deploy AKS Lab Services Only"&lt;/strong&gt; to skip Infrastructure deployment and directly go to service deployment.&lt;/p&gt;

&lt;p&gt;When the deployment workflow completes successfully, you should be able to access the Online Boutique from anywhere on the Internet at the pubic IP &lt;br&gt;
address displayed in the final lines of step "Deploy AKS Lab Infra and Services" &lt;/p&gt;

&lt;p&gt;To get access to the cluster via kubectl see above&lt;/p&gt;

&lt;p&gt;Finally, you should &lt;a href="https://kubernetes.io/docs/tasks/tools/install-kubectl/"&gt;install kubectl&lt;/a&gt; and &lt;br&gt;
&lt;a href="https://istio.io/latest/docs/setup/install/istioctl/"&gt;install istioctl&lt;/a&gt; if not present on your laptop yet.&lt;/p&gt;

&lt;p&gt;To install these on a Windows laptop, I've left a &lt;a href="///ps1/tools/Install-ToolsOnWindows.ps1"&gt;script&lt;/a&gt; &lt;strong&gt;'Install-ToolsOnWindows.ps1'&lt;/strong&gt; with snippets inside to show you how to install istioctl and kubectl on a Windows machine. &lt;br&gt;
They are both stand-alone executables so there are many ways to install them.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/MertSenel/microservices-on-cloud-kubernetes-azure"&gt;Github repository for the project:&lt;/a&gt; &lt;a href="https://github.com/MertSenel/microservices-on-cloud-kubernetes-azure"&gt;https://github.com/MertSenel/microservices-on-cloud-kubernetes-azure&lt;/a&gt;&lt;/p&gt;

</description>
      <category>devops</category>
      <category>azure</category>
      <category>kubernetes</category>
      <category>microservices</category>
    </item>
    <item>
      <title>Lessons Learnt Along The Way</title>
      <dc:creator>MertSenel</dc:creator>
      <pubDate>Sun, 27 Sep 2020 11:48:03 +0000</pubDate>
      <link>https://dev.to/mertsenel/lessons-learnt-along-the-way-55ag</link>
      <guid>https://dev.to/mertsenel/lessons-learnt-along-the-way-55ag</guid>
      <description>&lt;p&gt;It has been 11 years past since my first full-time position as a "Junior Networks &amp;amp; Systems Administrator". As of writing this I'm 31 years old, hence my corporate IT career journey started when I was 20 years old. A bit younger than today's university graduates. Funny how time has passed and today I'm in a position where I assist and mentor juniors both formally and informally in a workplace environment. &lt;/p&gt;

&lt;p&gt;Couple of months ago one of our managers came in and said they want to hire some "DevOps" interns and are planning to start a mentoring programme, where we pretty much come up with a curriculum of concepts and tools, we desire these interns to learn eventually. As a part of this programme, mentors who are already established DevOps engineers would become mentor's and provide assistance in those particular subjects in the curriculum.&lt;/p&gt;

&lt;p&gt;Needless to say I've raised my hand to volunteer to be one of the mentor's and already put in some work to come up with learning topics, materials, a schedule and flow for the topic and related activities as such. However, yesterday I was thinking, besides from all the discipline specific knowledge, what can I actually teach to a junior that would benefit them the greatest. What advice, what insight, what gem I could give them, that would be of guidance in their journey in the next 10 years.&lt;/p&gt;

&lt;p&gt;So it is yet again the million dollar question, if you can talk to your 20 year old self right now, what would you tell him or her about the upcoming 10 years. &lt;/p&gt;

&lt;p&gt;The things I will share today are insights I have accumulated over the years and proven to be true again and again. However, I have this point of view about giving advice and knowledge sharing in general. If you go to anyone and just pour your wisdom on them, it never creates the effect you wish it does. According to my past experience, all advice I've received and actually applied are the answers I personally felt the need for and seek it. This sort of information is valuable, if the receiver is ready for it. So I wouldn't necessarily preach these lessons on my younger self or any younger individual. Instead I would do what I'm doing today, leaving it in an accessible place for curious minds to discover.&lt;/p&gt;




&lt;h2&gt;
  
  
  1- Approach to Mastery and Learning
&lt;/h2&gt;

&lt;p&gt;The real difference between the master and the apprentice is the amount of time they have failed. &lt;/p&gt;

&lt;p&gt;There is only one method for mastering any aspect of your life, that is practicing. Everything easy now was difficult some time ago, only thing changed in between was repetition. &lt;/p&gt;

&lt;p&gt;Being smart only dictates how fast you can learn something new. How fast you can grasp something, and there are also different types of smarts, you need to learn each and respect every one of them. &lt;/p&gt;

&lt;p&gt;Expose yourself to new information, be curious and adventurous. You can only learn what you don’t know if you expose yourself. You can also never know if a piece of information can benefit you, don’t waste time trying to learn everything but don't see your time wasted when you’ve learned something you don't see an immediate benefit. &lt;/p&gt;

&lt;p&gt;Learn the gist of things, the main idea, the core principles of everything you do. There is not a tutorial for everything so you need to make your own decisions. To figure out complicated looking questions, you need to have principles you can fall back onto simple directional guidance that will help you figure out the answers. &lt;/p&gt;

&lt;p&gt;In order to achieve this, you need to focus on learning problems and solutions instead of tools. If you know what problem you are solving in the essence and what solution you would like to reach, you can achieve anything in between. If you are lost in between these, then you would only become an operator of tools. &lt;/p&gt;

&lt;h2&gt;
  
  
  2- Understand Seniority in Business
&lt;/h2&gt;

&lt;p&gt;No matter the circumstances, some things just happen with time. Experience is one of them, experience can only come with time because there is not a real methodical way to gain it. &lt;br&gt;
Some things you can’t learn by reading a book, it’s true. You need to deal with people, customers, deadlines, mistakes, conflicts, headaches, heartaches and a lot more professional and personal drama. They all shape a person’s experience. This is not something that I’ve immediately acknowledged during my initial years. Sometimes, just being in the room long enough, is valuable enough for a business. &lt;/p&gt;

&lt;p&gt;Also, do not forget that no matter what you’ve lived so far, your perspective is still only your perspective, valuing other people's opinions, at least giving them a fair assessment, increases your perspective exponentially. &lt;/p&gt;

&lt;p&gt;Ask right questions and you will get excellent support from any senior in any field. However, as time goes by time becomes more valuable. So you need to know how to approach them, and how to make best out of their time in an efficient way. &lt;/p&gt;

&lt;h2&gt;
  
  
  3- Don't Shy Away From a Challenge
&lt;/h2&gt;

&lt;h3&gt;
  
  
  There is something brilliant about being daring
&lt;/h3&gt;

&lt;p&gt;Awkward situations.You can actually tell how much you are getting out of your comfort zone by the amount of times you find yourself in situations where you don’t exactly know what to do. Every time you feel awkward, know that you are actually doing well. Obviously the exact same scenario shouldn't make you feel awkward in the long run but initially it will, and again it's something like after workout muscle pain, that's how some people feel more 'trained'.&lt;/p&gt;

&lt;p&gt;It's similar to anything in life. If your job becomes too easy, you need to do something harder, much like you would go to higher weights in the gym. You also can't stop challenging yourself. Even without increasing the intensity, you can have diversified challenges.&lt;/p&gt;

&lt;p&gt;An example is, while working on my technical skills in DevOps, I also take every other opportunity I can find to participate in sharing, teaching and mentoring in business and my personal life, because I want to give public speeches, write books and maybe run workshops in the future.&lt;/p&gt;

&lt;p&gt;I take on projects, where the technology is something I'm already very good with, but took on the project just to improve on my project management skills. Again, If you want to be a leader, you need to start leading things, you need to accept delegation and actually own and deliver and deliver good results to the people who have trusted you.&lt;/p&gt;

&lt;p&gt;There is no denying that I'm ambitious, and this ambition provides me a different perspective to spot opportunities otherwise may seem not really glamorous, or seem like a burden. I see a room full of growth opportunities.&lt;/p&gt;

&lt;p&gt;Yes it's difficult to do extra, but easy is not our friend. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_2Tjt_YH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/y4sbs3q4ghoxlrulgt0a.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_2Tjt_YH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/y4sbs3q4ghoxlrulgt0a.jpg" alt="Easy Paths Leads to Crowded Destinations"&gt;&lt;/a&gt;&lt;/p&gt;
Easy Paths Leads to Crowded Destinations

 

&lt;p&gt;Image source: &lt;a href="https://m.facebook.com/visualizevalue/photos/a.749620378758293/749620342091630/?type=3&amp;amp;source=44"&gt;&lt;/a&gt;&lt;a href="https://m.facebook.com/visualizevalue/photos/a.749620378758293/749620342091630/?type=3&amp;amp;source=44"&gt;https://m.facebook.com/visualizevalue/photos/a.749620378758293/749620342091630/?type=3&amp;amp;source=44&lt;/a&gt;&lt;/p&gt;
Check out Jack Butcher’s other work: &lt;a href="https://twitter.com/jackbutcher"&gt;https://twitter.com/jackbutcher&lt;/a&gt;



&lt;h2&gt;
  
  
  4- Don't Whinge
&lt;/h2&gt;

&lt;h3&gt;
  
  
  And own your destiny end to end
&lt;/h3&gt;

&lt;p&gt;20s are difficult, I get it. Transitioning into being an adult is difficult, yes. However the earlier you accept the facts the less time you would lose complaining. Life is no war movie, there are no reinforcements, no cavalry will come for you. You are responsible for your outcome %100.&lt;/p&gt;

&lt;p&gt;Complaining is just time lost, period. When you are a child, you can complain to your parents, if they allow you. It ends there. Unfortunately, this is a behaviour we bring from our childhood to our 20s, the earlier you snap out of it the better it is for your own sake.&lt;/p&gt;

&lt;p&gt;I’m old enough to know that chaos will happen, uncomfortable events will happen, my only chance is to separate my decisions from my reactions. This is only possible, if you accept and internalize the fact that you have no one to complain to. This is different from asking for help, ask for help but when things go south, accept your responsibility. You are far away from perfection like everybody else in the world. Just try to learn from them, and visualize the alternative. If you never experience negative events, you would be crushed on the first occasion. &lt;/p&gt;

&lt;p&gt;If you know that, complaining is not an option, as a reflex you can block your emotions. The better you can do this, the more that challenge transforms into a learning opportunity.&lt;/p&gt;

&lt;h2&gt;
  
  
  5- Funny How Ideas Change So Quickly
&lt;/h2&gt;

&lt;p&gt;Even throughout the last 10 years, my ideas changed on so many topics I can’t even list them. &lt;br&gt;
Be open to being wrong and be ready to update yourself. Your current views are shaped with your current experience, so don’t be a fanatic about them. Know that they are most likely to be changed in the future via various inputs to your life. &lt;/p&gt;

&lt;h2&gt;
  
  
  6- Be Authentic
&lt;/h2&gt;

&lt;p&gt;Being authentic is effortless and It provides mental clarity.&lt;br&gt;
I believe one of the reasons I did not feel the famous “Imposter Syndrome” is that I was, maybe sometimes too honest with my employers. &lt;/p&gt;

&lt;p&gt;I was very open with what I’m currently capable of and my potential during all of my job interviews. If I was inexperienced in an area, I would tell them how much time I would require to do some up skilling on it.&lt;br&gt;
This behaviour may have cost me some opportunities, but on the other hand I always had the confidence that I was very open to my employer, what I can deliver to them at the present and I am capable of picking new things as well. &lt;/p&gt;

&lt;h2&gt;
  
  
  7- Success Is Just Putting In The Work
&lt;/h2&gt;

&lt;p&gt;The success you have achieved, won't feel like success in the beginning not even in the middle.&lt;br&gt;
It will feel like you are chasing away a freaking train, if you look all the way ahead to the result.&lt;/p&gt;

&lt;p&gt;The champions, %99 of the time, are not doing glamorous things, they just wake up and train hard. They have no fans cheering in the gym, there is no press, but they know they need to put in this work to actually perform great in that %1 of the time when it matters.&lt;/p&gt;

&lt;p&gt;Success looks like getting sweaty in a gym, not like having an after party in a rooftop bar. That may be a byproduct, but it won’t feel like that until you get there. Whatever your goal is. &lt;/p&gt;

&lt;p&gt;Between my 20s and today, my curiosity or growth mentality didn't change, one bit. What has changed is execution. I realized that, unless you take action and do work, information is useless.&lt;/p&gt;

&lt;p&gt;This was possible when initially I took a chance on this theory that claims success is more about consistency and repetition of ordinary effort rather than bursts of extraordinary effort. &lt;br&gt;
Actually sticking to doing things even if I don’t feel like it, and see that it’s actually working. &lt;/p&gt;

&lt;p&gt;You can only see your success, either if people tell you so, or you really objectively take a step back, zoom out and compare yourself to your past self. In the present though, if you are growing you will always have that ‘itch’ and the feeling will suck. You will feel some sort of hunger, again like anything we are human and this is the human reaction. We want instant gratification, accepting it's not possible and that is the natural way is important for staying resilient. &lt;/p&gt;

&lt;h2&gt;
  
  
  8- Ideal World Does Not Exist
&lt;/h2&gt;

&lt;p&gt;Ideal job, does not exist, Ideal project does not exist, Ideal working environment does not exist.&lt;br&gt;
There is fine balance between having standards and expecting everything to be ideal. &lt;/p&gt;

&lt;p&gt;If you feel the world is coming down every time you encounter something different than ideal, you would be exhausted. Accept that there would be adjustments in your plans, almost all the time.&lt;/p&gt;

&lt;p&gt;Learn to lower your expectations just a notch, to protect yourself from emotional let downs. If you have it better, good for you, else well it’s fine we already settled on that it won’t be easy right. &lt;/p&gt;

&lt;h2&gt;
  
  
  9- Technical Debt is Not Your Personal Fault
&lt;/h2&gt;

&lt;p&gt;When you work in a company and you see there are things that don't sit right with you, be open about it and speak up but don't get stressed. Getting fresh out of learning, you may think that in the commercial world, everything would be a textbook. On the other hand it’s not always the case, purely because we live in a capitalist economy, and business needs to consider cashflow, period. &lt;/p&gt;

&lt;p&gt;Best practices are known and obvious but not always feasible. And drift doesn't necessarily happen due to people's talent shortage, ignorance or anything like that. it's simply a business decision, to prioritize something else and we are not the judge of this decision.&lt;/p&gt;

&lt;p&gt;The judge of that decision is the management of that particular business, we are responsible to inform others about the consequences of the shift, but knowing why it happens may put your mind into ease. &lt;/p&gt;

&lt;p&gt;Actually, your manager, the ex-developer, the ex-product manager most likely had an idea what would be close to the best practice in the first place, or they may have already realized a certain thing needs refactoring. However, business doesn’t work that way, maybe they were understaffed, or working towards a tight deadline for a feature. They may have simply not had the go ahead to go back to fixing things. &lt;/p&gt;

&lt;p&gt;If things gets out of hand, you have every right to raise concerns, but don’t expect to not have any clunky processes leftover in any environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  10- Understand the Business
&lt;/h2&gt;

&lt;p&gt;What exactly are you getting paid for? What is the value you are providing? What problems are you solving? How does your work translate it’s outcome to your employer/client? How do you reduce their risks/costs? &lt;/p&gt;

&lt;p&gt;Figuring out the answers to above questions is necessary because not all companies are providing clear success metrics to their employees. You may be lucky if this is more methodically approached by HR specialists but even in the absence of that guidance, be aware of your own position.&lt;/p&gt;

&lt;p&gt;In a business context:&lt;br&gt;
You are %100 responsible for your interests.&lt;br&gt;
You are liable to a business, you are professionally contracted to.&lt;/p&gt;

&lt;p&gt;These two are everything you need to take care of, you can't fall behind on your obligations to the business, that you are bound with a contract. It can be a direct, employee/employer relationship or it may be a direct customer of your services. It doesn't change the fact that, you can't fall short on these, this is your core integrity as a professional. Always keep this intent first.&lt;/p&gt;

&lt;p&gt;On other other hand you are %100 responsible for the reward or compensation you are getting in return for your value. These interests, perks or monetary valuation is something you are responsible to gain.&lt;/p&gt;

&lt;p&gt;Think about what your purpose is, when you are at work? Is it simply a paycheck, I don't think so right, you want to do more. Does work provide you the opportunity fulfil your dreams, if not have you asked if things might change or not? if still not why are you still there?&lt;br&gt;
It’s a two way street. You need to be skillful to be desired in a business context, however the job you do needs to be challenging and rewarding enough to keep you growing and motivated. &lt;/p&gt;

&lt;p&gt;Business is purely a value exchange, what do you solve for your client/employer, what do they then solve for their customers? How much better you made their ability to serve their customers is your value?&lt;/p&gt;

&lt;p&gt;Once you figure out all above, you then have more clear vision on your self-worth in business. &lt;/p&gt;

&lt;h2&gt;
  
  
  11- Your Best Work Will Come Out Of Your Centered Self
&lt;/h2&gt;

&lt;p&gt;There will be stressful times, where “shit hits the fan”. There will be downtimes, production issues. You will be tired, you will be mad, you will be frustrated. &lt;/p&gt;

&lt;p&gt;Going back to not complaining philosophy, I would also suggest blaming and pointing fingers is not an attractive skill to have in any area of life. &lt;/p&gt;

&lt;p&gt;What will make you a superstar is keeping your composure and trying to block off your emotions. Just think of solutions with your rational mind. By the way, solutions might be asking for help, however make all decisions with a centered peaceful mind. &lt;/p&gt;

&lt;p&gt;Rushing, and acting on things trying out different things without any particular good reasoning is not such a good idea. In any business context, keeping your composure under chaotic times is a desirable and useful skill to have around. &lt;/p&gt;

&lt;h2&gt;
  
  
  12- It is Your Responsibility to Stand Out
&lt;/h2&gt;

&lt;p&gt;Everyone is super busy, and has their own lives and responsibilities. People don't really have time to look out for your progress. You need to be vocal about it and keep track of your own accomplishments. &lt;/p&gt;

&lt;p&gt;Don’t expect the road will also be carved out for you. You are again responsible for aligning yourself to what is next for you in your life, career wise. You need to figure out the next direction and walk and leap towards that direction. &lt;/p&gt;

&lt;p&gt;Don’t expect people to give you credit just like that, that won’t happen. You need to be daring and actually try to stand out. It can only happen if you try and improve. &lt;/p&gt;




&lt;p&gt;There you go, 12 pieces of advice I would give to any 20 year old today who would be entering the IT world today. &lt;/p&gt;

&lt;p&gt;The insights above are all part of my belief system, but as like any teacher I’m also imperfect and I can’t always practice them as perfectly as I wish to be. I’ve titled this article as learnt, as I have a better understanding and solidified views about the topics above in comparison to my younger self, however I’m also a firm believer that learning never ends. &lt;/p&gt;

&lt;p&gt;Hence, anyone who is reading this, the above insights are my experience distilled from 11 years of industry and 31 years of life experience. We are all students in life and can all learn from each other in some way. Be good. &lt;/p&gt;

</description>
      <category>career</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Azure Architecture Scenario: Protect an Azure App Service with a Cloud Hosted WAF (DNS Based)</title>
      <dc:creator>MertSenel</dc:creator>
      <pubDate>Sun, 23 Aug 2020 07:53:35 +0000</pubDate>
      <link>https://dev.to/mertsenel/azure-architecture-scenario-protect-an-azure-app-service-with-a-cloud-hosted-waf-dns-based-2jn2</link>
      <guid>https://dev.to/mertsenel/azure-architecture-scenario-protect-an-azure-app-service-with-a-cloud-hosted-waf-dns-based-2jn2</guid>
      <description>&lt;p&gt;A sample architecture that shows how to restrict access to an Azure App Service from a Cloud WAF provider's IP Ranges&lt;/p&gt;

&lt;h2&gt;
  
  
  Project Overview
&lt;/h2&gt;

&lt;p&gt;This is a whiteboard sample architecture that shows how to secure access to an Azure App Service hosted public and client facing application/api behind a WAF that is managed and hosted outside.  &lt;/p&gt;

&lt;p&gt;This particular example was based on Incapsula/Imperva Web Application Firewall and their implementation but it would be very similar for most of the DNS based WAF providers.&lt;/p&gt;

&lt;p&gt;As the main requirements doesn't change: &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;First&lt;/strong&gt;, you need to proxy(pass) the traffic coming to your application via your WAF provider's infrastructure, that way you make sure that the traffic is inspected by the WAF and rules you defined are applied to it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Second&lt;/strong&gt;, you need to make sure your WAF can reach your origin servers &lt;/p&gt;

&lt;p&gt;and &lt;strong&gt;Third&lt;/strong&gt;, make sure you are limiting access to your App Service only from the Web Application Firewall's Outbound IP ranges.  &lt;/p&gt;

&lt;p&gt;This flow would be similar for a Virtual Network hosted applications as well such, as Kubernetes Clusters, Virtual Machines, App Service Environment in short anything where you can apply a Network Security Group. If this is the case one can just apply the same whitelisting logic of the outbound IP ranges of the WAF provider, to the inbound rules of the Network Security group.  &lt;/p&gt;

&lt;p&gt;The reason I've made this sample with the regular Public App Service is that, for someone who doesn't knows it's full feature set, they might consider it an hosting solution that can't be secured or locked down. They might say, it's public hence it's not secure think they need to either opt-out from using App Services or forced to upgrade to an App Service Environment.&lt;/p&gt;

&lt;p&gt;If you use another provider like Cloudflare where they also forces you do use them as your DNS provider, in this case the CNAME record is automatically handled for you, you just need to make sure you turn on the "orange" cloudflare proxy mode on for your particular subdomain and limit access to your App Service to only Cloudflare's outbound IP ranges. I had setup something very similar in the past with Cloudflare WAF and with Azure VM and Load Balancer.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F5p3e9h7sheubyptn5wir.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F5p3e9h7sheubyptn5wir.png" alt="CloudWafandAzureAppService"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Diagram Details
&lt;/h2&gt;

&lt;p&gt;1- Normal traffic from the users of our application. Their traffic will be processed by Web Application Firewall's engine and expected to be allowed.&lt;br&gt;&lt;br&gt;
2- Malicious Attacks made to our application will be picked up by the WAF rules processing and blocked.&lt;br&gt;&lt;br&gt;
3- All Access to the App Service outside of the Cloud WAF Provider's Outbound IP range is blocked by the App Service Access Restrictions.  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://viewer.diagrams.net/?highlight=0000ff&amp;amp;layers=1&amp;amp;nav=1&amp;amp;title=CloudfWafAppService.drawio#R7X1rd6pasvavWeN0nzGSwdXoRyMmIS24jBijX3ogEMUbHsEo%2FPq3agLKVSFqVtZ%2Bs7v3DnKZTOryVNW8VP1iG4vd41pdTSRLN%2Ba%2FGErf%2FWKFXwzDVDkG%2FuAZ1z9T5Wn%2FxHht6v6pyImu6RnBSSo4uzF1w47d6FjW3DFX8ZOatVwamhM7p67X1jZ%2B27s1j791pY6N1Imups7TZ%2Fum7kyCs3SldrjwZJjjSfDqKnPnX1io4c3Bl9gTVbe2kVNs8xfbWFuW4x8tdg1jjsQL6eI%2F95Bzdd%2BxtbF0ijzwRInV3T399J%2FV083dkJutaXFwQ3O8386HOt8Enxx013FDGqytzVI3sBnqF3u%2FnZiO0V2pGl7dAtfh3MRZzOEXDYfq3Bwv4XhuvEO37t%2FN%2Bbxhza01aYrVeaOqc3DedtbWzIhcqTIjtlKBK%2BO1qpvwTZFrtTuduruDa0FfjbVj7HLJQO%2BJC1JpWAvDWbtwixsKVsCg7YGd8Ab%2F3CTCybuQcWogQuN9Wwcqw0FA6BJEr9FfSvP3qmZoWhbNR1We46lsmhtqZUT4cQ7NQxjgAlIe4QFLUV%2FJA7r2tXKvGtX3TB5UtKoxes%2FmwR0wwaAuwwM%2BwQM2gwcVNoMH%2FPV4kNaDxtza6HCqX3%2BA%2F%2F5eWx8A%2F%2Bv%2FsX8xFaAHjbQQVEeFaw34bmOd4hmg7AoPzQUB9nukmQlw3lJHxvy3ZZuOaSGbRpbjWAu4YY4X7lVtNibsDqm%2FtJbRx%2BsBdx0L2a7aK9%2FWvJs7lI978rZ6eJYKz8Cxjr1l6%2F5P5sH%2BGP9i7ncgNUzj95PMDN17btTfbTRvNR14lKk%2BvVCaYH20GNkd9h8o9W04HzA1Z9SvbTSX%2F9AW2sdoKU1Fc%2F%2FcLPYce09ri%2B1mxD4vW8zLtMW82sM%2BPR8tX7yW19xI3aopPk2c0SPvtRfzjcrK08Hb%2Ffx399nSn162bbP6obM624pcay1q7tCtbQbQjgYyP2BevRZzuB7pizt8HNTExYTSn%2BqVlluDlrSN7kl%2Bf7wm31ZmH5IgfsAXONriZdF6k3no28Ro0PB90qFvS%2Flj%2BDjfjpiXVbRvGlObqW%2F3cG22gWP4Vnk%2BxP4xvc1o8Uq1qJfeS6Pm4XPaI9zbpbatxQs1bIhj45G2gXYVjR0uU9%2B71Dz%2FO6uuJNS3LRZ5s3%2FmyDeJW7j%2FA9uM0IEbPQJ9YnyBt7g8KwU8lJQZ3%2B7W%2FPvgPfri1dWY%2BcdoSplSl9tBW6uhQJnYrjSVtrLQM8XHA83by%2BFcW8qrEcPVxGl901bEjeTWLWna2cpKj5LN7Via1nftBrWVXGondWdA2wNN24uXjwHDT0b9HnzbM632dzO1zy9Bxjx9ocG9Y2j3fmsg3RTo09M9B31i1P4r21nUuN9dcSsK9bGsDDay0oG%2Fs43kdfAdlNHfoTzNR4sHc%2FTYcwaLh6nK6O6Ifd0MhdVm%2BKaNpUYdnu8xrWmPgr98aypR7YDewPvtoC9jG1tt8eoN356ZYVcEvXk2ZW6g2NWIFFTgLc6w%2F0J6Jj4%2BA3Vf5iAVH6PHmgu9nAK9GPwXem8Cx2bQ2of2CD16e%2BHhOlJ6ob51QMMeQIrmm1b%2F%2BQMk42OEXw1fP1zMqVafnmgLfTUyadCmIUW%2BPvnc2%2FNqyExyrr18aELQHgv3YfuPr%2B6gz0%2BH%2BB6lDtzVgQsvc%2F2xZkMfJ6MFUkDc4TNqvwNcQm69zvTFfK57oicJHRalZLRwvAHzsB0qq6n%2B9gyowUN%2FdxOj%2Fwrf%2F4zcdCWvTsldbIdfa4w80R57lQGLHJbY1nTAS9BC%2BvoMrwNneowk9LzEu4BD%2BnxE0GBOGQGtos8DjZfDN9ka9F8%2BdNAE0O3YdR3oMnyssa3%2BZK72dUsXCB12spt3Hz0xHueO%2Braao2ZI3Zz73nSkr4W0A63YJvrNwjOzEas5BrTRUsYJrQIeg2boLo2y4bQT32Qsar5cKPWNPB1zoPk8tP8xWKzmA%2FaFGjE7kL2HzYABrZo28d2EToDg9ogVa6IpL4bkfwPUFGvE6kttMZ8BnxaD%2Fs6LSfnz7yiOKSuCv8On4Qr0J6ILYEEIUgx9mUA%2BUdgHjRmCLlA1cfmCcuaNHh%2BoYXfsSA2Olbtb%2F68w3v0n%2Fs6nl5X%2BuJsDVbBXYH9e7KEiurKnueKU7%2BDXtJRmtcX69%2F02B1PjsXnXUHbL4Bn4IsBlQQSpGTjIJf0JpHkpL0D7HGzT791uoj%2F2LMA6utWgdiBlK3HKLUR2MmlvZ8f6QUE%2FGnp%2FbgOWbYaslO5LZ3agCgva%2FFZ35WmdbSmiGbnn2DsYeIeiPr7O9QZHngVeBNcPfYx8s6c%2BonThNwOF3ShFT7yJS7yJy3xTZxY9hvbugXpjHxeW5NiVphqX1EGQO7SKU8AUt9WXaW05nOhPr%2B4w0FeNfeEBoQFbhqvR47Yieh1PVjRWEhwPuPahMr2scw7o2gzeWUHutVkZOFpbD7s06CZi72416s%2Bp9lS6A1mE7xXHYqMO%2F97LsqCBtRjbMlos4UWUHgZUu8Ft5cUWuDOAcw%2BWLIBuKZN%2BcE9FNKtxalZbjJY4E3JbYyb43UhHvqWAxXzyZR89BNTbQBLtAfTzP0KTA%2ByjZW9sw71uaypSYJVjUoht649zCmgU0NrnI3gvbFuQzN9z2Rr2weuYIudnWZL43OhGjqE9DfnRCPqLxwLKwZhG7yGwHYg9tNwFefgaHgEmvlAq9Ec2OVpC%2FH18DW0ZDTQyW15U6rcfw8hvkNdlluYdeNHcthTtfF4oO18vun77AV%2BI3LcJhvWYyXz41tlI08FOErYfwR0JWQG7RKkh%2FfGY0H%2B2S9rSEcNvhn2ZAtx2Buyrqy8eKLCv84BHQGdAIPgO8A%2FskWBNwetiOsrkHr0YbFtR6jacg968WISu7ABoOZSkxtbn7ZMVp2tUTpTdFuhiBXhBjonMKVLcXqGfs%2BDR9nxoJo32n4F7wStG%2B1OPemML8PDs9oKH%2B3t3IFPuiHHmbVOiZWV432429%2BdC%2B9meNkEPD%2FKE8gJ%2Bia326ZUuWF74naFu77%2FrcYxenadPexvZFD%2BispHQ3Cz8VJoO8YaOS4vXmtbRKwFpQQ9G2oEfWlRaeOhVUlqUXGnJ1NYZl9BWSjK5XfuottrwTQfPRHY5lnhEB02D5zmmMMUOVlYRgWLiCYqhdy6CnswKWcC2UN%2FF7VLTLWwBFQn6I53oj7SRPcK1fA7mYy%2F4XQnsVTK9gDw9gud75%2BlRRG98G%2FeM8u9LwePY5%2B5y4HN0tt22hA5gWpMz4txN24YMbAJepPz8cti02sD9870NUCJYdbALgQ2w0BM7YAHqOsQpI%2BJ3dyqSB1L8CLww675Nb1L7b1OFJvBRc%2FUpwXLmc3a7SYF3yRSWA0CKuBx0Pm2D20LTTWg1RM0cdRTrL0XTAzK4gKG7djeGDFQb%2BgG%2BQEx2kna3CLb2QDPHp7AVIz5eEjTQzM4WOLuTBakgtoLkp7BVLoWtwIUkttJgPUlUmMJWqkO9TF8eX%2BLYSvnSdw9SMV%2BqTyAx0w5Ea3EvpoDXwrT86PFYNAWcAu9KgdiiQVEQUYEl6XjxiOqo%2FE6bCfkdZMpvtr8P%2F7rn%2BvvjbYd6fWgrgV7PKEK9FhmZabI6oZwuSs1t7HwSx%2FL6lxpTKNm%2F0aK2GZ7qW6O8j6E50vSUhYrqgQT%2BRo%2BTG0Wtp5iI6pp58aOLKB48T46JVHj1YPwN%2F%2BIojP%2FFWUj0Irw8ys1hE72xwOPfy7v4eBhZkcv4FhFKQTQ7PRlHnUEp8ErilMqOf%2FPsuHe2Heekpvzwosz2dtyn1NZHHXaAMsd2hXspfj7hu%2Bf2T7uIv368b%2FWyyAaxsciVQDYX%2FJmtrEhccWSDKD6BbNnR8e%2BnZ6BGJ3ieHCNyUOgbG8GIKY4u4jizRKLIJAflptwcUC%2BzTjyS9eU9ZkUzvJJkLPuoBT2RLeQX2C9XalAe9uLoKJYyg1hptpXdLeiLtsHx0nZhHejQcR0Qt2V0QBbOlLEr64AszL6hDog7xKoyOgDWnQarwZTQARxvjOrANGPcNF8HaBz9%2BSY6YFKefGokN6YDOIYnerJZVAd6bEIH8uLNbBlTzpSxa%2BsA%2BHnfUAc80IEydmAnuRzfViSqhA5MOwkdyI7Uc3SAyO%2F30AGZePgldECRNm1l5hX3hQZ8QgeoUjrgnSlj19YB7zJjlxfWARqi3JN2QEL%2FZ1rHmSoPjmmw2UUjYjYVEbtlI2Ltm0TEIkTEHfqKtOKk5Dg%2BvLXMOH5bGCfnqUuP47eFe%2BGl1%2BMP4%2FiEVraPPDpD5I4abxUhdj5jniqnh%2FRFRvNO9O4T%2BOZSVPskvkng2w62ZP64tJ87TkTFYqmouK18m6h4OnbkUnMUIgvHtOSNi2oC788hRzWhXWpGqy3MzpSz62vC7DLj2hfXhHaXIrOgxTWhpLer1LcJTeBLaAIvCeJ30QSvDppwcnwoqgnb1hQk3Cs6Wwe%2BVNImeOXmdtF3%2FeaaoJBVRN9PEzwJNKGUTSjr8ypNN64J0q6UJiidb6MJoiNPy9gEPJ7xklfUO5qhx5fUhHLekdI8U86urwnimeswrqYJLsWUsQm4iklSmpTkFtUEkUpogldKE1CKv4kmdEATytiEsrOMMze13seTS9oE6ez1PtfWBOnMSOZamiB3KeakTZjiW8Rt%2B1OaICVWX0p0KU3wxt9GEwaO7J2yCSL2zpWE2Wc0gUpFzF7JiNkf%2FfjWmtD5nhGzBxEze9ImRDRB8iRc%2FVp89JSXhXF89NTTSoyezmjpu8wgeO0GxZ6cQTiPVkpiTZg3LrGWYkafu1bhumspoH9nrvW40loKbwYYd2pNURTjyo6FzBjoVQLj2uVGUJVBcmXXHxpBBXxXxqfWFLnYNwlXsH9m5p1H3yuqB2B%2Fy%2BgBc66cXVkPmPa3XFMkC00HdwSU0IOyIyG4XjiuB1S5dcttRaO%2BiR64oAenZhJielB69p2XPS2hB2IZPWDPlbMr6wGX3P%2F2XfRAAj0oYw9Kj4NwctIeUJJXTg%2FGzDfRA6qlzMrYg%2FIz8HxbmCX0IHuNdI6c8efK2ZX1gD93Dey19KDntE%2FugojuesI93BLOihTVg%2FQ%2BFurIzHJmZEX2sVw49sP94FK4w49pKdpG8vSp1KC2%2BK0tsnO250hdjuxLx50fgAEbSZCCHQVjOCfuOh7OqWCUJm5AYm3cXSSZHEgj7qboAWZ0gDukbdHfXWUV0r3DftAO9G12ygZBjMd5ktIrbHekrSQ0E%2FqWs6Y7HSVREFEGXC2we9SThHpi9Wwne0Y1tXs0I1r06jjTEosWSTzmiVkrRQBxe6zs1aM7yVLnyuwkO4y%2B%2BLtI4tgqUegrfW5HSYdr%2BXJ%2BFGPhnYCUg%2BOrXY9xIjG33Sm4jzeTE7tU3I77ZkmU9aWcwH09XpwT%2FrjtJznBg6ZvT62hksl6M82TGzgKAPirNJnkvrVcVETaJWdJqNxo8Tl6nD2eCPpM5jej44kgUVNtl7kvSJAAzbXtFfYFkT2VCV4E%2FPkML3pbwgvzJC92QFOerOXB3S2emPY8jmhFM6EVveyVzUV2t3t197vvbic7YF2gUn8LWqJ5EOMhrjAjf7ejDd4Y%2FB3iLgn%2FN%2B4SpIg9Y3HcN%2BXxFN3htd8LKvu7Z6kTaEeh9QVMcZGv2NOSFi6xplPOygeRbeHAhoMPK5yaHULfobmTFLEUBouJeYNe9hri7KgAn0%2Bus8P1ayQOTEsSNOPpj3I8KgAPZ5bQUX%2B065M66qGOyidGmMkYLUQScnGM9FKzBnRuBJVnozz0u2PeAo7YerMsG%2BV2ZhL7Mo2PLQOe8IlcA77l7R7VgkypkjEywn0GR6VqDN7MjAFUKrjPGCTdGyQkPWd%2FaZ7tSM1F4f4ojc%2ByHdLsWZCpARW3wSQqiM1FBevuiudkiF7z4sckE9YYE6fh%2F%2B%2FfraXTDVKp0eFvPyMnzV0oE2C1Fs8EGObhi2Shu2NuuUpGLkD%2BSmnowvx2kTR0fWMEJ%2Bqr1dzUVJIxjqnMMa3faA1HYzx6MNfGVsXWT2Sgi%2BQIzM4md2aKunTuuez0dPB74jiYNrWO9GIeNH3J3JqatXw3l7qxvtXgZcwDSVjHPOB5G%2F5uJ3Aa%2BXtj2oTepnazNUY3E8t2zOX4xjbW0H%2B8kwcyPrwHVAnO39BM9Xa1HEfyI2p%2B3r6ktF1etBiWTolWLSO%2FYe1a6Q3DTLARuQLKqavV7QK%2B0zaWxvzWMbRJSoKABk5ccKLZJAO2p%2BiZFK6FqevYYmbiynhqy%2BMClkpZSZF%2FvggwKom0lTyfTt%2FKZ7D1alkrK2xG5tAAHWJ8rPzfxnICqtzYhCygeRTNrXaENuH1EFFC4XA1TIK5Vd9vTStsGXo6Cu%2F7y%2BXluvLBsRnpfb9UPmi%2BQHpfQx8boeZYa2dija2lOm8eziYofrinZaHRIFyeGo7jBvRUN44VlwFjqdcxATbyfK7atqn5Jx%2FMeXhLPqf8K2HG6yzdxt9ZTwLf1u4bdvuWD38Ogq8gP4Rd7Jcb%2FPJJhHQ5LglARmuz1owCwOuo67HhnFTltGytjTlY%2FY94Ty4PJGn7EKq7vVKXhbEkBSQ3%2BwS6wO6VZQL9gBjGemEuVcew8cfEwN4twdVQsZ%2FdbuuQQ524OxSIE0O9b0DuMMsutVpbmmEfnl0b%2F7cxbIdwyJ1bqn4bdZE%2B2%2FMI3PkkiJ%2B%2BRNPnk7YxMbQZUkLb2OigATE2c0LWf9UbQMcGkh8xdG7aju3%2FBsjUZpHfIGwtc2HiT0pdYsbjhbU2%2Fv3tv%2F8SPdnTcC9ML3th2poAOKH0voRkbffr3d9wwN4CaN%2F%2F%2B5LkOPdDBtYGJYHQdOWnq0ZjBBAFf6ztcq9cDTS37xhO4A3Wmugf4Tv5cE21Q810Y02ZePWd3A%2Bt3u5fuVDxvq3qKzcq68Y2SAMmtmOtAj023%2F0n4b%2B6tfwfn8j2JHwIopV1pMcBEMT6GvBE9Rtf6z4qO%2B51Nf4cHyfIwZ4dbv3d7g1f1L3h2WuFNSmrNfBlp%2B5t1r42j0H0SM72suKR5RYfz%2FeeinUjEkJMH%2Fl8%2Fv4XL2Q5wWGQnmRdGDGbizEQaW6OkFS2pmIO91dz7WzU%2BX%2BxY35Dt5jW%2FZTcXDrerVQTkRGdFo1qhmRUr%2BX4sunACGBsZ0Y8DmetvgOuhOATgE57bY4JFnbJoMG%2F7H8jgH0Qv%2BNJUX53C6BBXCtDkv%2BRMAhLbpB%2F0G%2BH503HvV58zCXLOtQyACKrtAZ7tfg4XVmD2KClhbbnvtVu%2FKcppMfTfh9EJSYkfxPngzcdZ3oUB6gLSACYiFuGjQnBHZ0eJKGzantw1SsJwV01JQT11SpQcRMIexnb8CmnNquhhY0jcf4ITFGX5A%2BYHqDhfwMK2oWMTukI%2F4IGqlrh475LNQ1NWa4Lfy1kuksjU0Ioqd9zZPgpRl%2BJu31j9OQPq%2F91vL2j2T%2FL22p6Eicr7krbHRJanR%2FH%2FO%2BpQf0%2FgBaRT%2F%2FrBIoPp2VOCFRYX%2FDyApX2ZutaMAT2Ytg4EYYRdrQyVdSI3KoYDcGxDb6Efbs0nJRE%2FJSq%2BilV9Q8rVYVb9khCkKuVqnqo4HLkN2a40hav7KC%2Fs1vL4PuFfZkqXLyz1Z5w8QWWy9EbI1LsqI5lp3BJLyWZwd%2FYkv9XkJtne9ilzUFfXpOlwn1%2B1l7wc92tk3JXkqdtZEEM%2FpIlYEDHbcWXtR4uV3OHbzJ873C%2BX2Ti7fLK%2BIwHyEcsBLUvECQxQcEq81DMqoblsFY6WUCCSd6GK71BO4O356Xa5xxog9Ifa05m4an%2Bbj5cdvwFKInntEW4SCl9DdrbBu2RAkVZRamG7OsK%2Bp5V9IjIQ7Qwly8XqUSltLZ8Bn2H%2Fj4%2B8MM3ERf1MEOvx8lCL7Xpl%2FDX62wkZcxIXpNJXSeFmcYb2evQkpdKLvT%2FecGqJi6pw%2B1m7vcpWEX4XK5glVJnixSs6nGYRLhcwaoeD62fLlgV7U18yV3mMjKQfEztaP6e%2BcglNg%2FIlbU8Mbm8TVZmtNSl3BNUYlvTnt8vBRf0dfhi6RYInaKLdOm20stLwZNK60i%2BjZSuO6%2BURrsnUi9CJO2ky%2BEmcFdyObf9KOHGEb5Fvkuz5QZHNt7jcuiWMAapFh24nwaLQhbiDgAh2grZMMK1pjO6JcBzgmi3GxwP9MFz3MCr27ihsE02pdSZlgDehtJBnoI2b3f4voEnbtq42WQquS2hB9cHNm5AaTe2dAuLAwgD6M%2FzFN6Dm1HoFm5UUWa2bHIkgRgu%2FPXvmfkLhB8lIjNkA4sCz%2BMGFAHkDTfMCNB2E5cGduCbJOwjvBP66MF7vSZ%2BCy6%2F5mUKaEE2z812cB9ulrelqbQBC4rLTHm4Bm2IyTIjJxcOo%2BQDFRtFFgD7PI9utSdPH18AvCDFH7dEll3q1KYbHmhHisLJ3ZSO5S1rpUEO%2BHiqMPK2%2FGWtGXomNyjmRN%2B2Qd%2FI3%2FTi5KO9c5O9y01ac0Tbzk10LDdfBEzWX48UriGI5qGGSChhuN3Gl0LPl8IxkWyUQrhn134aOLgwWg6LwDTug%2B1dHZDKOpYBDTHbpxZu%2BfJexXZfCu%2BxD9fxXXi95597G%2BACWSbQUEpCn%2FhwT6xdJXwO%2BtdW5HQ6kdTS4fRGPZTdHlrNwnyUtvFt2ISPuRtMcviIhRrP5eO91GzuOoq452O76%2FMReMPIrE%2FHLiAb0NTn18zXLMO8P9xLtr3u%2BSjiloeBizQfewSdsDyjQIpdBsekvSiPOMlEfgxYcq2L6Ak8gXtVXHA9fZ2Se1zc5oDXyTZALjiGe18ZfzvEIOhDfdzxmrgFEN4FyDsd4ztxqzzn%2F27Ce2YB38XsNDLpJeN5S8gjuCTjxoQi6Z2Jje1hDwotI%2Fe5PU7gZfaWwGz51OB9zZMbJpAivqal%2BpVv%2B73mNmH78zYQZm3KQW3YJb3k0ptyqOd7sL0gyfstuD5qBAiEG0EBUQItl0IUooltJvc0Od0k21iYUIJE9BmQFgLaTvAXGmGbERTpjcHGh%2Fds99fJuyIoo6N99YZT%2Fx5ALyV6T7TdVymGpMm02FmbPjKtUJts8y6BSLu03ctNFpuPSOno7AeR%2FigigRe5O5kyqfEJNIoVnUKEySlcmoVGU%2BKxXgmNRC%2BORoO87YJ5aHR2wakfNEqgEQMe2K6Uf%2BS2Gwk0YnLT1OSjkXduVPmDRhdFI0YyKfeUfxRQzSHl0QvHbZInpyUmL4lhtoy60LfPj9scR8vYFkBEwJxSo5lJNhDVpLOTbHRmmBTyIVpaOBr5CD6qBJruhkjU48PoTVImKSkC6XT8CCuIwpRIFBYgSVeYT%2Ff3HKI0fFcUaRiiPQvJv0doOrF7ou32tjE0TZVDTWxjzNgWi1YB%2BoqWoqhV6ewSViW7rEq%2BVaHOLrNINbcd5eFemu6tCh%2FQwUNkCFCdoEtoPbpEi18rYnN%2FL9HuvVUhiVJepyRxjICWiPKRpcEx7fAY24tZjLHro5Vm79GKINKr1cYt0yRCH%2FtRP14nyVrGwXEEKQ%2BRv4Ajy9LUR5u2AH%2FxGWUcoI9mx5C0iBU6sjU6SN6CPMTxtlPjSNtWA8fhxIxxmiPSwiakJTv5bLZkYsnQq40gSVQ76eczuclecy2rrJxbJuhnBCnpE2M50XFhiyIrnYRFyU7qmm9Reqm5r9IWRbm%2Fl3pN92BRQp9BwoRXTOAPRfyPoUg0%2Bc0aH%2B4lGr63KMSX6pNRbt%2BfUQi6%2BP6MskeamLVoCyJBLH%2FUvXdApf6YQ81Fn9hHN3Ld8dEtOI6g5QEVJSza7AaIw%2BE7%2FZH%2B0JfbxtD0pAVK%2Fc4YO0ff72Sa5zqmukM%2FrXCchuUE4nKSndA2WyZn0KdrjRoN3ARGZpcvybeojHxuKpefOC3uA7OkTGmZUSMmNR%2FB5hZWybcm07PnI37itEvGaSzYu5OlOtul5tUkVkpLSm5EnymbWD70RKqpz8ZnQME4SnrZc5H51lQ7uzzQT3wW9YI1v6xoYWsyTsxBaNmFT%2FKtCXf%2BHMRPfHbZ%2BEyjsJhFkdKyn4nRxkxCYvIi%2BizpxIKfu6vFaFxq9JMtPzt8%2FpoaudHBkiu95k%2BM5vvDWIpFkApbldQKFy9nxjbXqpxffvEnRrtgjOZpwP%2BTRal4%2FL6ycRquyYnLSk48nymXY%2BjXdebTZKGeGPnU8pLS5ljWzvb8kc%2BfOC3uC7e7pMxuYYvS2crJ9UZsfkLbHItC4QqUnzjtG8VpHNhSuuh8mkzQsmi81tmlVqhx%2BZF9hoxyWPbzSvNplJQY%2FWwL2clj8ywraPT5o58%2F8VrEI4aYQ%2BkVnk%2BD%2FvBxqzLOS7ObZ1Xcs0tK%2FMRrF47XoI9K79R8GonXisdpsiDSCUnJi%2ByzpBI0qHe1ubSOmxr95Eqvxqak80c%2Ff%2BbSYv4wSLwDlC1uTTwxYU16Ja2JRP3Ead8nTmsLA1JGu6Cn46VGW7j8GfEsT0fGss5X8nQg%2FkrIZrl5XhlQ6sfTuainA7ozKOHpdJL2q2z8TJ1dTPHH0%2Fm8pxOXifjV5C%2FcQ%2B9xmJfhvKy4V89yfkcl8x1XUnkyKhl5MipXS7zCpfJkXCwr02Xyauek7aC6hrOJJnsapV57XnKq5tL2EyJayzlyUJ3PrW08v9lExZxofgZOi2TBUx0DE2y%2Br0li2QZmAv8V5kP97ScFXWNqRaq9cUaYBQQORcyNWtf1dfCZ6nJ86QRbl2rmrFYaWKpgvFn7FRnw3qvxDpSwoi4wgcpyZK%2FIBeqLT2F22XogMjehLPiCIP62b%2BgLfSnz7b40eX9mNrYENZgLUYP9dtRI8p290Jcuv92Xxi8mPntfgeVyRuU8%2BrXqXeXTlMAvF4ylmy3wVL3VylODCwHxT7rlTO%2BK5%2BPeFZ9VTSIrqS5DXSvdcjWrSkCCW0i4VRmKBJn%2B1VHYAlWWUjWeifuhdxl1WeisvLNhwtrLU4rP9USRMIX1Ju13hHn%2BMTvjUl1EkqKn8wl2ZfFXmEf93lzq5nIcUTq%2FI%2F%2BIOi6lgpojop0WsVMixFVv%2BTuaY6rhf68lUJUrCZRCkmGTVLOUvc89SvL2235QEsrXvsTE5CB6EWc6XjgokYCyEbk36iKPDE0NawVk9WNrEtJgtQoDTa1fDsQvK7BZz%2F2G%2FaIV0ABejGVRxduWhqHbh6d0a6GSBN9a4MGTGIecsdY6yfNN8oGrEKmtnF%2BJGiPYwkLVjdu0sqXP3O8%2Fbov%2F8Q8bcl1qkgY1eF%2F4tn1lFPzhZyKPOB2xYEuQu5HHG2HrfuC2sYmOB51W3sAtgFAzeBNI4sTSY4ARCINqb0xSNcW3875zsxmFtApOh5%2B5lyBqtTbezd3tMQchjzjicv%2BlVphz3S%2FUFn5RwPtR9LvStSwi9SFy%2B3FRsPuTHkapVN2lcM49CmenUJCp0beRh2iOuxIMhplYL17wTHJ%2FJYY4LuLjJkdGyHiInYWE32uQhMaogGHubhm6cstTWOvmgeEuFebHGqcv2zh7zcY5bPwW%2FrlQe%2FzNBRurXLKxu0s2trz5xpCbDFTyIfgCQR2XGDKnw2jnVHWEfd2My0NqujxCiltfUCLw%2BHwG8yu%2Fyt%2BhxEWZ2n1nVNoLHv0d%2BGzJaifhI35twOCuA5MOD4aBfmIWhUqkEfdLB6baIczed%2F%2BMGpFUOlbt2RnljeKZ4e%2BpWxYebFDkX15AVxRPojg1qFssPBk5T37d3tGJm7HsYCNx0r%2BzxmecrexPksT0J5OUnyx3nMpInopoyUcb6%2BaH4X87EdUgWfpiNwa4mdxqpq1ZdO12Y%2FvTbBEgI3LKU3dsClgC9CmHTXkqcOmZPTYukiyV9gNrfBqkatcqi01TWeFvEqSyiozmIgxbgryHOqK%2FolVED0VFr19HlGYCl%2FpkIVHm9EjGsWoYn0XBUNRC1z8UnUpCJoqi4k2y3FyFS%2BDrtXGRTscaDXdE4tO644DKZmDkP6zgumU5E2OBAcvDb%2FkRr4B8b%2FB%2Bv5B6qeUJaf%2BuqP5dv157ouBHBt7RWYXL6Kt5ZTSdrvlxDPECil4R7ugTYHco53xp6KMrfwf08Qnou%2Fsk9DF31PGGro58BaztlyFdtIThOXBHn4S7jXmzVD%2FMMVnTgYsaHmicWXrQ5pZtgAWu%2FmHUuwDQ0QnR4pg%2FXAeNptOF0L4O566GWFRBxApU7Y9BFlu5rdUYqspxLHNH3%2FEJO0h%2FMqT94wgWesv%2FKARjTyLYO0jDjeqvKrwhJ29AvR7uakhixdRm%2F5XU9ey%2FAvznn4ln%2Bxn9qP5wGfpzrQkKmsma%2Bf%2BTftvpMDWOg8bOdLAR%2BpbimOC3306FoYPfh4bwhxv58dtYm0BJ5O%2FFUTUcqDqNqr76%2F%2FUxcDIETjV0dRhNO4J1slLsxZ8OT0%2FrWhvHNnWyhhgLLYeL5n7tlwL%2FBMz%2FhIAZAt%2B7uGhmTWR8ccjMFFiedm3opc%2BB3kuCJVMtCJZ%2BcdE%2FhZVVtkrmEbJGm8vCZZVJBDfsF48YMgWm0v46r%2FMnbkbRqiYscY1L%2B5lfGjczf1%2FcXMBvqxaGtz8aN1eSwyhcoomikFXjuVuWqx3%2BOY6E1wYwtpwFvfA02yXNH%2Ft3mD%2Be5m8pOi5Ld5XPyVKFpU%2B2dXUB%2BidawJ9xl4zNKext3Gvj2FraIlYzFKd6LYvIfheLuB9BiY2e8EfHTi5pRdmiVrTyZ7EvuX38DgejuaQt%2FBthkMuavL3EMmWOutSeywdrPTJ13dhvZowvRk7ojbVx5uYSsIrs1QpkNqEN8L8HJNL9eK3qpnG4lre2ff%2BArtqT%2FdrBk2ifs5QriXLRpaUZanxYS59hCpIrvNStzd2uDV8ERQ37cw8%2F%2FaP4XWg9%2FrtaW46lWfOTw1EZsH4BdK7SiYiaTs%2FyfWlihv2IaEQhROTT0nD%2BnH9wgSHB066Boy7HFvEJbubmaK0iNR%2B4qj80SAhwszVGNyMwSrax%2FgbeweWzhMRkMXPF8xcPFHIFPIU%2FsI81Odx%2FV2FTpMo0yFfU26yAsKwhS%2B%2BKCTd8dcmGr3%2FZ%2Fy5khP6uPalld4MfE9WjLlqmSDDXE4msEK9IPoRt0D2UC5I6KG%2Fv0nkbWhc2bjkN9qGq3mZtwDHYBMO%2BJdYmZ0vgFXIR%2FMNF0I1L2imJvNpeezp82%2FmjVuUTbR3bmBLGgYfYb7%2BOMn8OfT%2BHf5g7GkSvXX%2BpeSXA%2FNOBJvdn11smdincJReIF40Y6cTgfiW5k%2Bfa4WKYvyIiwP4ub5nssk%2FOsb9AIDL%2FIJvbcb%2F4SYjK3HjyR3HrsGXrdHh2vgfKFJi8yXRB2Uq%2BTJ4JWfmZ6n4GCH4GCI6q6QU0gk5EGlz1Dw8QVNII%2BL%2FlvcqltV6o80zHMjMvhIqftzbeY63GY3zd0uzbhamtLdt6d4IQ31jebDC2J94l%2Fl2tbvY5S6K%2FSGjvbBxrbarzGz%2BByk2YVYMFmxL8uAFu%2BkkscAYdKJX6hG%2FSqxAI1E86zlmG45gexrdSvvP4v9QYRQpo%2FH8uoyo8F1cVJiMnRiVrx%2FbVpv4zFuUl0%2B0ESWL8zC%2F2hQxCyWAoyq%2Bv8yyOIfkXSwqdsYssU1LYa0lKZqieM9oaDFxFuZnFCxt84ZDAyA0wcA5ABVnvS5Pf87m6ss3R3nZqE3Out1QXPITwNeGvE7xMpH9IB2LZhj7F8iMgkiUKme7yNeQjI6FfpnzQVxvMKTIQGgoIfKgD9uIFxziX4yKykpYFfW2tlDCsxBPJ7fVUml86b1R1VGXCXR8CiECQw9DpIz%2FWAc32LUeyFfhZCRIJCfh9LoGVtQZhWALvVZOw01BtZ4tLnNMCVUgWjijk0YA5GyGuNWmeOXRykbRy7sr4lTfWdkEJi465ZwjPXtnLCY8FzH2fk9GiiR8DXdiSHNHG42O9mWksryYddEo6%2FBGJfzQDLzd0msfQXYKZUQZTGWtmrsfgdNj1Sg5%2FOHwRDnN0Nc1hmvlKDhdII%2FBj4a9v4d04n08Z%2FKtNlmQMPAYZSX80vhCD%2BdNr5rITBV%2BNo%2FmppnNHzjByvwnCK%2FThgggLjooOBuTk%2BS02GPQjW%2BVkq6y%2FcD1ZSw89hYLgapg8dau%2B35rpOakfhn%2BK4YXdh%2BsxvMCGkB%2F34cvch7ui7gNzLXlIjyiSJON%2Fpb4fH330r5Kc8FljzF%2BLE5%2BtTnA1OUgPHPo55HP8gr9SPr4Pn0s7ANfieyU9XrivSCKElRVeI9OG8FMU%2FvdHAC4jAMUdgmsJAJ1hAVLcjU%2FYnZjzTczyJLg5UrWqnp6VIYtDOI7Xi1I7Z2omugJon0soNvdCX8u1ojMw9MkvyohFQH6vLQfXnGB5E%2FhP3Z%2FUj5c3wYdNNb1ALKxjgDWCSHmTvjHyn50f1PLBXBtblfQTs9r%2Fy58yvldt%2F4n6w79TfP0blpZFdJGtpnX2IsncY1KTtU7melLDMPlbywrP2NSOVJosNGMDHTVXdh67IvJRcvkye0dRjcav5Fwvk8aFikpRZGNvFHtrWZKWEID9juDLp8TxnwjXiVTS%2B2G5r5y8YTJz4H1CUjLLdP6IyWXEhLtLLwL4YjHJ3%2BJznpiwP2JyhpjQid3RWXs4LyQo8HNtIev21x5xuapk6Qbe8f8A" rel="noopener noreferrer"&gt;To view the diagram in draw.io viewer please click here.&lt;/a&gt;&lt;/p&gt;

</description>
      <category>azure</category>
      <category>devops</category>
      <category>security</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Azure ARM Template Development - Validate with WhatIf</title>
      <dc:creator>MertSenel</dc:creator>
      <pubDate>Sun, 16 Aug 2020 06:24:36 +0000</pubDate>
      <link>https://dev.to/mertsenel/azure-arm-template-development-validate-with-whatif-opo</link>
      <guid>https://dev.to/mertsenel/azure-arm-template-development-validate-with-whatif-opo</guid>
      <description>&lt;p&gt;Hi, I'm back with yet another script I would like to introduce and pretty much document for my and other's future reference.&lt;/p&gt;

&lt;p&gt;Today's script is named "Deploy-AzARMTemplate.ps1" and it's geared towards one-click deploys from your local development environment to pre-configured cloud dev/test environment. Making use of the new &lt;code&gt;What-If&lt;/code&gt; validation the script also allows us to pre-validate the deployment.  &lt;/p&gt;

&lt;h3&gt;
  
  
  Purpose
&lt;/h3&gt;

&lt;p&gt;So what does this script does? or what is it for? This script's main purpose is to allow DevOps engineers to be able to make a full deployment to a pre-set environment for testing purposes. The script is easily modifiable and can work with multiple projects, given they are using similar naming structure.&lt;/p&gt;

&lt;p&gt;The benefit of this portable and configurable script is to reduce the time spent on curating different &lt;code&gt;AZResourceGroupDeployment&lt;/code&gt; cmdlets,&lt;br&gt;
such as &lt;code&gt;Test-AzResourceGroupDeployment&lt;/code&gt; , &lt;code&gt;Get-AzResourceGroupDeploymentWhatIfResult&lt;/code&gt; and obviously the real deal &lt;code&gt;New-AzResourceGroupDeployment&lt;/code&gt; which generates the deployment.&lt;/p&gt;

&lt;p&gt;I was aiming to set the parameters once and then just run the script by itself or with some easy common switches such as &lt;code&gt;-Test&lt;/code&gt; , &lt;code&gt;-WhatIf&lt;/code&gt; or &lt;code&gt;-Force&lt;/code&gt; to be able to perform these functions.&lt;/p&gt;

&lt;p&gt;This script is aimed to be used locally while developing/extending your ARM templates and allow for quick deployment to a Dev/Test environment. Hence keeping your Azure Environment information is not insecure as long as your repository is private and only people who you want access has access to it. Azure Subscription ID or Tenant IDs are not exactly secrets on their own, but it may expose you for some vulnerabilities if they are known together by a 3rd party.&lt;/p&gt;
&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;p&gt;Make sure you are on Powershell AzModule version 4.2 or higher. The script already enforces this.&lt;br&gt;&lt;br&gt;
To learn more about the new WhatIf operation you can read more here: &lt;a href="https://docs.microsoft.com/en-us/azure/azure-resource-manager/templates/template-deploy-what-if?tabs=azure-powershell"&gt;https://docs.microsoft.com/en-us/azure/azure-resource-manager/templates/template-deploy-what-if?tabs=azure-powershell&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  The Script
&lt;/h3&gt;

&lt;p&gt;Without further do let's get into it.&lt;/p&gt;

&lt;p&gt;I'll break down the Script into sections first.&lt;/p&gt;

&lt;p&gt;First let set our parameters block and configure the path where our ARM template files resides and also Azure Environment Information&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="c"&gt;#Requires -Module @{ ModuleName = 'Az'; ModuleVersion = '4.2' }&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;CmdletBinding&lt;/span&gt;&lt;span class="p"&gt;()]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="kr"&gt;param&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Parameter&lt;/span&gt;&lt;span class="p"&gt;()][&lt;/span&gt;&lt;span class="n"&gt;switch&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;$WhatIf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Parameter&lt;/span&gt;&lt;span class="p"&gt;()][&lt;/span&gt;&lt;span class="n"&gt;switch&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;$Test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Parameter&lt;/span&gt;&lt;span class="p"&gt;()][&lt;/span&gt;&lt;span class="n"&gt;switch&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;$Force&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c"&gt;#region Configuration&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c"&gt;#Deployment Assets Configuration&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c"&gt;# Go Up one level so you can pick a project folder.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c"&gt;# This is here in case you want to keep your script in different folder.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$OperationsPath&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Split-Path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;$PSScriptRoot&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c"&gt;# Target the folder which desired ARM Template and Parameter files are in.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$ArmArtifactsPath&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$OperationsPath&lt;/span&gt;&lt;span class="s2"&gt;\infrastructure"&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c"&gt;#Azure Environment Configuration&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$TenantId&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'#{YOUR-AZUREAD-TENANT-ID}#'&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$SubscriptionId&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'#{YOUR-AZURE-SUBSCRIPTION-ID}#'&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c"&gt;#endregion&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we make sure we are in the correct Azure Context&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="c"&gt;#region Connect to Correct Tenant and Subscription&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$CurrentContext&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Get-AzContext&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c"&gt;#If there is no AzContext found connect to desired Subscription and Tenant&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nv"&gt;$CurrentContext&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;Connect-AzAccount&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Tenant&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$TenantId&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Subscription&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$SubscriptionId&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-UseDeviceAuthentication&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nv"&gt;$CurrentContext&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Get-AzContext&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c"&gt;#If subscription ID doesnt match, call the Set-AzContext with&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c"&gt;#SubscriptionId and TenantId to allow switch between tenants as well.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$CurrentContext&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Subscription&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-ne&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$SubscriptionId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nv"&gt;$CurrentContext&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Set-AzContext&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Subscription&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$SubscriptionId&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Tenant&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$TenantId&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c"&gt;#endregion&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then let's configure the service parameters. These sections are all seperated so everyone can just configure that section.&lt;/p&gt;

&lt;p&gt;For example, here use a ResourceGroup name structure where I just purely concatenate the components but you may be using a whole other,&lt;br&gt;
structure like having '-' in between those parts.&lt;br&gt;&lt;br&gt;
The idea is, we have all the different identifier enums about our service so we can curate whatever string we want use.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="c"&gt;#region Service Constants&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$ProjectName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'mert'&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$Stage&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'dev'&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$ServiceType&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'fnc'&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$Region&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"wus"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c"&gt;#endregion&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c"&gt;#region Service Curated Parameters&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$ResourceGroupName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$ProjectName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$Stage&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$ServiceType&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$Region&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'01'&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$TemplateFile&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ArmArtifactsPath&lt;/span&gt;&lt;span class="s2"&gt;\template.json"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$TemplateParameterFile&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ArmArtifactsPath&lt;/span&gt;&lt;span class="s2"&gt;\parameters.&lt;/span&gt;&lt;span class="nv"&gt;$Stage&lt;/span&gt;&lt;span class="s2"&gt;.&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nv"&gt;$Region&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;01.json"&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="nv"&gt;$ARGS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;@{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nx"&gt;ResourceGroupName&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$ResourceGroupName&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nx"&gt;TemplateFile&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$TemplateFile&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nx"&gt;TemplateParameterFile&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$TemplateParameterFile&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nx"&gt;Mode&lt;/span&gt;&lt;span class="w"&gt;                  &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'Incremental'&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nx"&gt;Verbose&lt;/span&gt;&lt;span class="w"&gt;               &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;$true&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nx"&gt;ErrorAction&lt;/span&gt;&lt;span class="w"&gt;           &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'Stop'&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c"&gt;#endregion&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Deployment Helpers, to only run certain mode. They are both good for certain things.&lt;br&gt;
The &lt;code&gt;Test-AzResourceGroupDeployment&lt;/code&gt; performs a very primitive check and it's already covered in regular or what-if deployments but it is still somewhat useful.&lt;br&gt;
These can sometimes detect problems that VsCode ARM Extension cant, so it's here to make the script complete but using this mode completely optional.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Get-AzResourceGroupDeploymentWhatIfResult&lt;/code&gt; is something I use a whole lot more nowadays as it attempts to simulate the real REST API response from the Azure side, assuming the deployment was made.&lt;/p&gt;

&lt;p&gt;As you would see in Microsoft's disclaimer as well that it's still in preview mode so it may still generate some extra noise.&lt;br&gt;
So practicing with it now, when it's out of preview, you will be ready to use it to it's full extent!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="c"&gt;#region Deployment Helpers&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="bp"&gt;$Error&lt;/span&gt;&lt;span class="n"&gt;ActionPreference&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Stop"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$Test&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;Test-AzResourceGroupDeployment&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="nx"&gt;ARGS&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="kr"&gt;exit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$LASTEXITCODE&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$WhatIf&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nv"&gt;$WhatIfResult&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Get-AzResourceGroupDeploymentWhatIfResult&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="nx"&gt;ARGS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt;                                    &lt;/span&gt;&lt;span class="nt"&gt;-ResultFormat&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;FullResourcePayloads&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nv"&gt;$WhatIfResult&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="kr"&gt;exit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$LASTEXITCODE&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c"&gt;#endregion&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally the part that performs a WhatIf first and deploys.&lt;br&gt;
This is pretty useful as you pretty much be able to see a &lt;code&gt;Delta&lt;/code&gt; of your changes.&lt;/p&gt;

&lt;p&gt;If &lt;code&gt;-Force&lt;/code&gt; flag is passed to the script then, the &lt;code&gt;-Confirm&lt;/code&gt; flag is set to false hence the deployment will just proceed without confirmation.&lt;/p&gt;

&lt;p&gt;This is useful if you know you already know that your template is valid, healthy and deploys fine but you are just making changes to it and want fast deployments.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="c"&gt;#region Deployment ("Validate &amp;amp; Deploy" or "Forced" Deployment)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="kr"&gt;try&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nv"&gt;$PromptForConfirmation&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nv"&gt;$Force&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;?&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;$false&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;$true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;Write-Host&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Deploying &lt;/span&gt;&lt;span class="nv"&gt;$Region&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nv"&gt;$Deployment&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;New-AzResourceGroupDeployment&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="nx"&gt;ARGS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt;                                                &lt;/span&gt;&lt;span class="nt"&gt;-Name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="n"&gt;New-Guid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;" &lt;/span&gt;&lt;span class="se"&gt;`&lt;/span&gt;&lt;span class="s2"&gt;
                                                -Confirm:&lt;/span&gt;&lt;span class="nv"&gt;$PromptForConfirmation&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="se"&gt;`&lt;/span&gt;&lt;span class="s2"&gt;
                                                -WhatIfResultFormat FullResourcePayloads
}
catch {
    Write-Host &lt;/span&gt;&lt;span class="bp"&gt;$_&lt;/span&gt;&lt;span class="s2"&gt;.Exception.Message
    exit &lt;/span&gt;&lt;span class="nv"&gt;$LASTEXITCODE&lt;/span&gt;&lt;span class="s2"&gt;
}

# Tell me If my Deployment was Successfull
if (&lt;/span&gt;&lt;span class="nv"&gt;$Deployment&lt;/span&gt;&lt;span class="s2"&gt;.ProvisioningState -eq "&lt;/span&gt;&lt;span class="n"&gt;Succeeded&lt;/span&gt;&lt;span class="s2"&gt;") {
    Write-Host "&lt;/span&gt;&lt;span class="nx"&gt;Deployed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$Region&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Successfully&lt;/span&gt;&lt;span class="s2"&gt;"
}
# Or if there was an error during Deployment I want to know about it
elseif (&lt;/span&gt;&lt;span class="nv"&gt;$Deployment&lt;/span&gt;&lt;span class="s2"&gt;.ProvisioningState -ne "&lt;/span&gt;&lt;span class="nx"&gt;Succeeded&lt;/span&gt;&lt;span class="s2"&gt;" -and (&lt;/span&gt;&lt;span class="nv"&gt;$Deployment&lt;/span&gt;&lt;span class="s2"&gt;.CorrelationId)) {
    Write-Host "&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Get-AzLog&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-CorrelationId&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$Deployment&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CorrelationId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"
}
# Else just let me know if there was no AzResourceGroupDeployment Object found
elseif (!(&lt;/span&gt;&lt;span class="nv"&gt;$Deployment&lt;/span&gt;&lt;span class="s2"&gt;)) {
    Write-Warning "&lt;/span&gt;&lt;span class="n"&gt;No&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;AzResourceGroupDeployment&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Found&lt;/span&gt;&lt;span class="s2"&gt;"
}
#endregion
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this section, we pretty much use the &lt;code&gt;-Confirm&lt;/code&gt; switch built-in to the &lt;code&gt;New-AzResourceGroupDeployment&lt;/code&gt; cmdlet to achieve the prompt behaviour.  &lt;/p&gt;

&lt;p&gt;The rest of the code is for catching exceptions for broken/invalid template deployment(or WhatIf) attempts and print the error/exception message as needed.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is Cool About WhatIf
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;WhatIf&lt;/code&gt; check makes ARM templates look like &lt;strong&gt;terraform plan&lt;/strong&gt; command that tells you what would change.&lt;/p&gt;

&lt;p&gt;However, terraform works with a state file, here the state file is the actual state's of the Azure Resources, being retrieved upon request via the REST API.&lt;/p&gt;

&lt;p&gt;Terraform, populates the state file itself so when you do a deployment after the first time, the state file is compared to declared deployment. This is a very accurate difference as both are generated by Terraform.&lt;/p&gt;

&lt;p&gt;You need to use the deployment mode "Complete" instead of the default "Incremental" if you wish this new &lt;code&gt;WhatIf&lt;/code&gt; check in ARM Template deployments works closer to Terraform.&lt;/p&gt;

&lt;p&gt;But because you are comparing the actual live resources to your ARM template hence it's not easy to always 1:1 match the default generated properties to your ARM template, or there are some properties which are just metadata.&lt;/p&gt;

&lt;p&gt;Hence currently there is noise in the output but that doesn't make it any less of a tool to be used for Testing your ARM template if it will pass an actual deployment request made to the Azure ARM Rest API endpoint.&lt;/p&gt;

&lt;p&gt;Or to simply visualize and see the impact printed out for you after you have made some changes, updating or extending your ARM template.&lt;/p&gt;

&lt;p&gt;The full script file can be found at: &lt;a href="https://github.com/MertSenel/devops-powershell/blob/main/scripts/local/arm-template-development-helper/Deploy-AzARMTemplate.ps1"&gt;https://github.com/MertSenel/devops-powershell/blob/main/scripts/local/arm-template-development-helper/Deploy-AzARMTemplate.ps1&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Script in Action
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Folder Structure
&lt;/h4&gt;

&lt;p&gt;I use a folder structure like below, I'm sharing this to give the script some more context, in terms of how the files are named.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--vytu_WGg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/vmonkmcy184vn5pzmsoy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vytu_WGg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/vmonkmcy184vn5pzmsoy.png" alt="Sample Folder Structure"&gt;&lt;/a&gt;&lt;/p&gt;
Sample Folder Structure



&lt;h4&gt;
  
  
  Test Mode
&lt;/h4&gt;

&lt;p&gt;.\Deploy-AzARMTemplate.ps1 -Test&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ZCEplwms--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/bwq5hzxgnxxwgtqwpe6x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ZCEplwms--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/bwq5hzxgnxxwgtqwpe6x.png" alt="Sample Folder Structure"&gt;&lt;/a&gt;&lt;/p&gt;
Test Mode



&lt;h4&gt;
  
  
  WhatIf Mode
&lt;/h4&gt;

&lt;p&gt;.\Deploy-AzARMTemplate.ps1 -WhatIf&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--SbYz3Ure--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/ia98u1ydrfpubo7t629i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SbYz3Ure--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/ia98u1ydrfpubo7t629i.png" alt="What If Mode 1"&gt;&lt;/a&gt;&lt;/p&gt;
What If Mode 1



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--SM-LAUWt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/ji3gqbocddesyiblr1a7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SM-LAUWt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/ji3gqbocddesyiblr1a7.png" alt="What If Mode 2"&gt;&lt;/a&gt;&lt;/p&gt;
What If Mode 2



&lt;h4&gt;
  
  
  Deployment Mode
&lt;/h4&gt;

&lt;p&gt;.\Deploy-AzARMTemplate.ps1 &lt;/p&gt;

&lt;p&gt;Performs WhatIf and presents the changes before proceeding with the deployment.  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3gxYpShB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/38jl8tsdukfpb5nldjy9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3gxYpShB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/38jl8tsdukfpb5nldjy9.png" alt="Deployment Mode - Pre-Deployment Confirmation"&gt;&lt;/a&gt;&lt;/p&gt;
Deployment Mode - Pre-Deployment Confirmation



&lt;p&gt;if "Y" is selected proceed with the deployment&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--0kO6F6NL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/bpayu2mbvov938961t1g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0kO6F6NL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/bpayu2mbvov938961t1g.png" alt="Deployment Mode - Proceed with Deployment"&gt;&lt;/a&gt;&lt;/p&gt;
Deployment Mode - Proceed with Deployment



&lt;p&gt;if "N" is selected deployment is aborted.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--7Yusl7zI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/kyv0f5f9v4zo2lmfbkt5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--7Yusl7zI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/kyv0f5f9v4zo2lmfbkt5.png" alt="Deployment Mode - Abort the Deployment"&gt;&lt;/a&gt;&lt;/p&gt;
Deployment Mode - Abort the Deployment



&lt;h4&gt;
  
  
  Forced Deployment Mode
&lt;/h4&gt;

&lt;p&gt;.\Deploy-AzARMTemplate.ps1 -Force&lt;/p&gt;

&lt;p&gt;It will do the same as above but, won't ask for confirmation. Just runs WhatIf Validation and if no error thrown, proceeds with the deployment.&lt;/p&gt;

&lt;p&gt;You can think of this like &lt;code&gt;terraform apply -auto-approve&lt;/code&gt; command where it skips the interactive approval.  &lt;/p&gt;

&lt;h4&gt;
  
  
  Conclusion
&lt;/h4&gt;

&lt;p&gt;This is very handy script to move around with you in your different project and configure it once to your desired ARM Template project and you can always deploy it to your desired dev/test environment with ease.&lt;/p&gt;

&lt;p&gt;This script can also be extend to accept some sensitive parameter values via adding a &lt;code&gt;securestring&lt;/code&gt; parameter to the script and also extending the Hashtable.&lt;br&gt;
This is forward the secret value to your ARM Template deployment as input in a secure manner, so you dont have to save it in plain text. &lt;/p&gt;

&lt;p&gt;You can use this to pass any type of secrets to an ARM template.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="kr"&gt;param&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Parameter&lt;/span&gt;&lt;span class="p"&gt;()][&lt;/span&gt;&lt;span class="n"&gt;switch&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;$WhatIf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Parameter&lt;/span&gt;&lt;span class="p"&gt;()][&lt;/span&gt;&lt;span class="n"&gt;switch&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;$Test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Parameter&lt;/span&gt;&lt;span class="p"&gt;()][&lt;/span&gt;&lt;span class="n"&gt;switch&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;$Force&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Parameter&lt;/span&gt;&lt;span class="p"&gt;()][&lt;/span&gt;&lt;span class="n"&gt;securestring&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;$mySecretValue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="nv"&gt;$ARGS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;@{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nx"&gt;ResourceGroupName&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$ResourceGroupName&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nx"&gt;TemplateFile&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$TemplateFile&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nx"&gt;TemplateParameterFile&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$TemplateParameterFile&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nx"&gt;Mode&lt;/span&gt;&lt;span class="w"&gt;                  &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'Incremental'&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nx"&gt;Verbose&lt;/span&gt;&lt;span class="w"&gt;               &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;$true&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nx"&gt;ErrorAction&lt;/span&gt;&lt;span class="w"&gt;           &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'Stop'&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nx"&gt;mySecretParameterName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$mySecretValue&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also convert any of the constant to an array and deploy to let say multiple regions with single loop and convert this script to make more than one deployments.&lt;br&gt;
It's flexible after this point.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="c"&gt;#region Service Constants&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$ProjectName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'mert'&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$Stage&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'dev'&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$ServiceType&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'fnc'&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c"&gt;#$Region = "wus"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$Regions&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"wus"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"aue"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"neu"&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="kr"&gt;foreach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$Region&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$Regions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="c"&gt;# The rest of the Deployment code logic goes here.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c"&gt;#endregion&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Another benefit is that, having this source controlled and not testing your deployments via your terminal will also give you more confidence in the configuration you are passing to your deployment.&lt;/p&gt;

&lt;p&gt;Last benefit is, you can add breakpoints to your script to debug a certain value at any given time if you are having troubles getting your deployment working. &lt;/p&gt;

&lt;p&gt;As always I hope this script helped you out and let me know if you have any feedback or criticism about it via comments section.  &lt;/p&gt;

</description>
      <category>devops</category>
      <category>azure</category>
      <category>powershell</category>
      <category>arm</category>
    </item>
  </channel>
</rss>
