<?xml version="1.0" encoding="UTF-8"?>
<!--Generated by Site-Server v6.0.0-e4519f5562eb6c2da2d6a1a416a85bb0a74bf9d5-1 (http://www.squarespace.com) on Sat, 08 Jun 2024 00:09:41 GMT
--><rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:media="http://www.rssboard.org/media-rss" version="2.0"><channel><title>JOT Insights - Jot Digital</title><link>https://www.jot.digital/blog/</link><lastBuildDate>Wed, 08 May 2024 23:37:06 +0000</lastBuildDate><language>en-CA</language><generator>Site-Server v6.0.0-e4519f5562eb6c2da2d6a1a416a85bb0a74bf9d5-1 (http://www.squarespace.com)</generator><description><![CDATA[]]></description><item><title>Microsoft Dynamics 365 Bridge to the Cloud 2 Promotion</title><dc:creator>Shannon Campbell</dc:creator><pubDate>Wed, 08 May 2024 23:37:06 +0000</pubDate><link>https://www.jot.digital/blog/microsoft-dynamics-365-bridge-to-the-cloud-2-promotion</link><guid isPermaLink="false">647fb19e93051a0534471f45:66215170b81b27326eed8167:663c0b82892d542e7d8018db</guid><description><![CDATA[On January 1, 2023, Microsoft launched the “Bridge to the Cloud 2” 
promotion to support Dynamics customers moving from on premise versions of 
Dynamics GP, NAV, SL and AX to Dynamics 365 online offerings.]]></description><content:encoded><![CDATA[<p class="">On January 1, 2023, Microsoft launched the “Bridge to the Cloud 2” promotion to support Dynamics customers moving from on premise versions of Dynamics GP, NAV, SL and AX to Dynamics 365 online offerings. </p><p class="">This promotion enables eligible commercial Dynamics on premises customers with an active Enhancement Plan (EP) to obtain discounted cloud licenses through the Cloud Solutions Provider Program, with partners like Jot Digital.&nbsp; </p><p class="">Bridge to the Cloud 2 aims to facilitate a smooth transition to the cloud for Dynamics users. If you’re eligible, it’s a great opportunity to take advantage of the discounted pricing. &nbsp;</p><p class="">Reach out to Jot Digital to learn if this promotion is right for you. </p><h3><strong>Key Details </strong></h3><p class=""><strong>Eligible Products: </strong>This promotion applies to customers migrating from most Dynamics on-premises perpetual products purchased through DPL to functionally similar Dynamics 365 online per-user offers available on NCE for commercial customers with a 3-year term. </p><p class=""><strong>Pricing:</strong> &nbsp;Enjoy a 40% discount on the standard commercial list price. </p><p class=""><strong>Signup Period:</strong> January 1, 2023 – December 31, 2024. </p><p class=""><strong>Promotion Duration:</strong> Fixed 3-year term from enrollment. (Non-renewable) After promotion ends, normal pricing will apply.</p><h4><strong>Licensing Requirements: </strong></h4><p class=""><strong>Initial CSP value.</strong> Customer must purchase eligible Dynamics online licenses with an annualized greater than or equal to the annual EP renewal amount of their existing Dynamics on-premises license.</p><p class=""><strong>Maintain CSP value</strong>. Customers must maintain the minimum CSP license value during the promotional term. Customers who fail to meet this requirement, including as a result of cancelling or reducing their CSP subscription are liable for EP costs from the date that their paid EP ended (as well as 3% lapsed fees) and may not continue to receive promotional pricing/terms. </p><p class=""><strong>Active EP plan</strong>. Customer must have an active EP plan for the on-premises system being migrated (at the promotion enrollment). Customers with lapsed EP plans are ineligible (beyond a 30-day grace period). While customers with an active EP plan may enroll in the promotion at any time, no credit will be provided for the unused portion of the current EP plan’s term. </p><p class=""><strong>Functionally similar product. </strong>Migration must be to a functionally similar product. Permissible migration paths. <em>(i.e. Microsoft Dynamics NAV on premises to Dynamics 365 Business Central) </em></p><p class=""><strong>Dual Access Rights.</strong> (For Business Central Licenses Only) During the promotional term, customers may continue to use, expand, and upgrade their legacy on-premises system while completing the migration without purchasing additional EP. Other EP benefits like affiliate license transfer remain available during this period. (Note: Expanding the number of on-premises users requires purchase of additional on-premises licenses).</p><p class="">This promotion is intended for customers committed to migrating to the cloud. Customers returning to or remaining on on-premises licenses after the promotional period who want Enhancement Plan (EP) benefits are subject to normal backpay and penalty policies.</p><p data-rte-preserve-empty="true" class=""></p><p class=""><strong><em>NOTICE: Microsoft reserves the right to discontinue this promotion, and/or to modify these policies and the promotion’s terms, conditions and expected launch date, at any time. </em></strong></p>]]></content:encoded><media:content type="image/jpeg" url="https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/1715211319564-TGO3J1VGIEJTG57244OA/Picture1.jpg?format=1500w" medium="image" isDefault="true" width="1500" height="375"><media:title type="plain">Microsoft Dynamics 365 Bridge to the Cloud 2 Promotion</media:title></media:content></item><item><title>Start Planning: Migrating from Dynamics GP to Microsoft Business Central</title><dc:creator>Shannon Campbell</dc:creator><pubDate>Tue, 07 May 2024 23:21:00 +0000</pubDate><link>https://www.jot.digital/blog/start-planning-migrating-from-dynamics-gp-to-microsoft-business-central</link><guid isPermaLink="false">647fb19e93051a0534471f45:66215170b81b27326eed8167:663c051b0c2f2409485a58c3</guid><description><![CDATA[Microsoft Dynamics GP has long been a trusted ERP solution for small and 
medium-sized businesses. Yet, with the emergence of cloud technology and 
the shifting demands of the digital age, businesses are now presented with 
a compelling opportunity to elevate to Microsoft's new ERP experience 
Business Central.]]></description><content:encoded><![CDATA[<p class="">Microsoft Dynamics GP has long been a trusted ERP solution for small and medium-sized businesses. Yet, with the emergence of cloud technology and the shifting demands of the digital age, businesses are now presented with a compelling opportunity to elevate to Microsoft's new ERP experience Business Central. &nbsp;</p><p class=""><a href="https://www.microsoft.com/en-ca/dynamics-365/products/business-central">Dynamics 365 Business Central</a> represents the next generation of ERP solutions from Microsoft, designed to empower organizations with greater flexibility, scalability, and innovation. With its cloud-native architecture, intuitive user interface, and seamless integration capabilities, Business Central offers a modern and future-proof platform for managing your business. </p><p class=""><a href="https://www.jot.digital/enterprise-systems">Jot Digital</a>, has a specialized team focused on Microsoft Dynamics 365 Business Central. We are helping clients move from their on premise version of Dynamics GP to the cloud.&nbsp;&nbsp; When we asked what compelled clients to move, these were their common reasons:&nbsp; </p><h3><strong>Why Consider Microsoft Dynamics 365 Business Central?</strong></h3><p class=""><strong>Flexibility and Scalability:</strong> Business Central offers cloud-based flexibility, enabling remote work and adaptability to changing business needs.</p><p class=""><strong>No More On-Premise Infrastructure:</strong> Say goodbye to managing servers and hardware. Business Central is fully cloud hosted.</p><p class=""><strong>Seamless Integration:</strong> Connect with other systems effortlessly, streamlining your operations.</p><p class=""><strong>Modern User Interface:</strong> Designed to enhance productivity and eliminate silos.</p><p class=""><strong>Business Intelligence and Analytics:</strong> Interactive dashboards provide real-time visibility into your data.</p><p class=""><strong>AI Capabilities with Copilot:</strong> Leverage AI for smarter decision-making.</p><h3><strong>Quick Comparison Table GP vs Business Central </strong></h3>





















  
  




<table class="tg">
<thead>
  <tr>
    <th class="tg-0hty">Feature</th>
    <th class="tg-0hty">Dynamics GP</th>
    <th class="tg-0hty">Dynamics 365 Business Central</th>
  </tr>
</thead>
<tbody>
  <tr>
    <td class="tg-fymr">Hosting</td>
    <td class="tg-0pky">On-premise or IaaS (not included)</td>
    <td class="tg-0pky">Cloud (included) with an option for on-premise</td>
  </tr>
  <tr>
    <td class="tg-fymr">Upgrades/Updates</td>
    <td class="tg-0pky">Free service pack updates for GP 2019 October release and later; earlier versions require manual upgrades</td>
    <td class="tg-0pky">Free and automated updates</td>
  </tr>
  <tr>
    <td class="tg-fymr">Add-on Options</td>
    <td class="tg-0pky">Large pool of add-ons</td>
    <td class="tg-0pky">Large and rapidly growing pool of apps</td>
  </tr>
  <tr>
    <td class="tg-fymr">PowerBI Integration</td>
    <td class="tg-0pky">Requires some effort</td>
    <td class="tg-0pky">Requires little effort</td>
  </tr>
  <tr>
    <td class="tg-fymr">Budgeting</td>
    <td class="tg-0pky">Setup and import Excel-based budgets, advanced capabilities through ISVs</td>
    <td class="tg-0pky">Create multiple budgets for financials, revisions, analysis, and dimensional reporting</td>
  </tr>
  <tr>
    <td class="tg-fymr">US &amp; Canadian Payroll</td>
    <td class="tg-0pky">Yes</td>
    <td class="tg-0pky">Yes, through apps</td>
  </tr>
  <tr>
    <td class="tg-fymr">Dynamics 365 Sales/CRM Integration</td>
    <td class="tg-0pky">Significant effort</td>
    <td class="tg-0pky">Little effort</td>
  </tr>
  <tr>
    <td class="tg-fymr">Financial Reporting</td>
    <td class="tg-0pky">Management Reporter and Jet Reports</td>
    <td class="tg-0pky">Leverages account schedules and analysis views, can also use Jet Reports, or PowerBI</td>
  </tr>
  <tr>
    <td class="tg-fymr">GL Account Structure</td>
    <td class="tg-0pky">Multiple segments for structure</td>
    <td class="tg-0pky">Dimensional chart of accounts</td>
  </tr>
  <tr>
    <td class="tg-1wig">Purchase Orders</td>
    <td class="tg-0lax">Requisitions, PO creation, separate inventory receipt, and 3-way PO invoice matching</td>
    <td class="tg-0lax">PO creation, separate inventory receipt, and 3-way PO invoice matching, Does not have requisitions.</td>
  </tr>
  <tr>
    <td class="tg-0lax"><span>AI Capability</span></td>
    <td class="tg-0lax">Lacks / Doesn't natively include AI features</td>
    <td class="tg-0lax">Built-in generative AI with CoPilot</td>
  </tr>
</tbody>
</table>


  <p class="">Business Central is the trusted solution when migrating from your on-premise Dynamics GP to the cloud. It is time to take advantage of a modern user interface, scalable platform, integrated functionality and continuous innovation. If you are interested in migrating to Business Central or want to learn more about ERP - Jot Digital is here to help. Contact the JOT Team at&nbsp;<a href="mailto:hello@jot.digital">hello@jot.digital</a>. <br></p><p class=""><br></p>]]></content:encoded><media:content type="image/png" url="https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/1715210471389-WNK6FI0MUWQ3J3601SAD/Plane+for+article+.png?format=1500w" medium="image" isDefault="true" width="657" height="489"><media:title type="plain">Start Planning: Migrating from Dynamics GP to Microsoft Business Central</media:title></media:content></item><item><title>5 Important Reasons Why You Should Not Use WordPress for Your Website</title><dc:creator>Todd Willsie</dc:creator><pubDate>Thu, 18 Apr 2024 17:01:03 +0000</pubDate><link>https://www.jot.digital/blog/5-important-reasons-why-you-should-not-use-wordpress-for-your-website</link><guid isPermaLink="false">647fb19e93051a0534471f45:66215170b81b27326eed8167:661985ebe72a86450c07b775</guid><description><![CDATA[Wordpress is used by 33% of all websites on the internet. While it has many 
great things going for it. It's time to take a real hard look at the 
security risks that it has going against it.]]></description><content:encoded><![CDATA[<p class=""><strong>Author Update:</strong><br>In 2020, I originally posted this article, about why you should reconsider using WordPress for your website. It’s been 4 years since I posted this article and I thought “Hey is this article still relevant?.” So I took a bit of time to fact check myself and I can safely say that things have changed… Just not for the better. In some statistics, such as WordPress versioning, things have improved. But in other statistics, it would appear that all of the reasons for avoiding WordPress in 2020 are still relevant to today. I have taken a bit of time to update this article to ensure that statistics and sources are up to date and to correct any information that was inaccurate.</p><p class="">— </p><p class="">I'm sorry for the click-bait title, but this article is written just for you, and I wanted to make sure you didn’t miss it.</p><p class="">Why? Because you're on this page. Most likely you've googled "Reasons to Use WordPress," "Why should I use WordPress" or "Is WordPress Really Made of Rainbows?" and you ended up here. Which means, you're thinking about using WordPress for your website.</p><p class="">And why wouldn't you consider it? It is used by <a href="https://w3techs.com/technologies/overview/content_management/all" target="_blank">43% of all websites</a>. That is a content management system (CMS) market share of 62.8%. The kind of market monopoly shared only by Microsoft's Internet Explorer for browsers in the 90's, or Apple's early 2010's US smart phone dominance.</p><p class="">WordPress has many great things going for it. It's open source, it's well supported, it's SEO optimized and, most importantly, it's free. But the internet doesn't need yet another article about how fantastic it is. What you're here for is another perspective, maybe one you hadn’t considered before.</p><p class="">It's time to take the red pill and see how deep the rabbit hole goes, as we look into 5 important reasons why you should <em>not</em> use WordPress for your next website.</p><p data-rte-preserve-empty="true" class=""></p><h3><strong>REASON 1 - Less than Half of all WordPress Installations are Updated to the Latest Version</strong></h3><p class="">Even though WordPress actively encourages users to upgrade to the latest version, only <a href="https://wordpress.org/about/stats/" target="_blank">45.2% of website users have actually done so</a>. All users who hesitate to upgrade their website are jeopardizing their security and putting personal information of users at risk. Furthermore, most businesses will buy a website with a shared web hosting company such as GoDaddy, where their website is likely on hosting that is shared with one of the sites that is running an outdated version of WordPress. And while many web hosting companies have security measures in place to keep shared hosted sites from infecting each other it doesn't remove the reality that...</p><p data-rte-preserve-empty="true" class=""></p>





















  
  














































  

    
  
    

      

      
        <figure class="
              sqs-block-image-figure
              intrinsic
            "
        >
          
        
        

        
          
            
          
            
                
                
                
                
                
                
                
                <img data-stretch="false" data-image="https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/1fa7c7fa-2e40-4e7e-a480-4738b3fa05e1/blood-pressure-gauge-business-computer-263370.jpg" data-image-dimensions="3860x2573" data-image-focal-point="0.5,0.5" alt="" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/1fa7c7fa-2e40-4e7e-a480-4738b3fa05e1/blood-pressure-gauge-business-computer-263370.jpg?format=1000w" width="3860" height="2573" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" onload="this.classList.add(&quot;loaded&quot;)" srcset="https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/1fa7c7fa-2e40-4e7e-a480-4738b3fa05e1/blood-pressure-gauge-business-computer-263370.jpg?format=100w 100w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/1fa7c7fa-2e40-4e7e-a480-4738b3fa05e1/blood-pressure-gauge-business-computer-263370.jpg?format=300w 300w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/1fa7c7fa-2e40-4e7e-a480-4738b3fa05e1/blood-pressure-gauge-business-computer-263370.jpg?format=500w 500w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/1fa7c7fa-2e40-4e7e-a480-4738b3fa05e1/blood-pressure-gauge-business-computer-263370.jpg?format=750w 750w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/1fa7c7fa-2e40-4e7e-a480-4738b3fa05e1/blood-pressure-gauge-business-computer-263370.jpg?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/1fa7c7fa-2e40-4e7e-a480-4738b3fa05e1/blood-pressure-gauge-business-computer-263370.jpg?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/1fa7c7fa-2e40-4e7e-a480-4738b3fa05e1/blood-pressure-gauge-business-computer-263370.jpg?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs">

            
          
        
          
        

        
      
        </figure>
      

    
  


  





  <h3><strong>REASON 2 - 96.2% of all Infected CMS Websites are WordPress</strong></h3><p class="">That is a scary statistic (it was 94% in 2020). <a href="https://sucuri.net/reports/2023-hacked-website-report/" target="_blank">Sucuri states</a> that in most instances, the compromises that they analyzed had little, if anything, to do with the core of the CMS application itself but more to do with improper deployment, configuration, and overall maintenance by the website owners. And to further add to that, many compromised WordPress sites, don't show their true colors to the website owners. Many of the compromises I have seen have implemented scripts that determine if you've hit your website directly, or the admin page first and whitelist your IP so that you are shown your website, while your customers who will have come in through a google search or a social media network, will instead see a web page selling prescription medications for male erectile dysfunction. So why does WordPress, which technically is quite secured by itself, get compromised so quickly and so often? It might be because...</p><p data-rte-preserve-empty="true" class=""></p><h3><strong>REASON 3 - WordPress Isn't All That Free</strong></h3><p class="">WordPress is the America of CMSs, it's free until you actually need to do something. WordPress does one thing really well, and that is blogging. Everything else? Well you're going to need a plugin for that. Want to sell some "Make America Greaterest" hats? You'll need to install one of the 680 shopping cart plugins available to you. Want your customers to subscribe to get a different color of that hat every month? You'll need to buy a $200 extension plugin to go on top of your shopping cart plugin.</p><p class="">WordPress has (as of 2024) <a href="https://wordpress.org/plugins/" target="_blank">59,470 plugins</a> available. This seems all great and well, and it's nice to be able to have so many options in a convenient central location, similar to the Apple App Store, or Google Play Store. But there is a major flaw with this. The plugins in the directory are all free, and thus the plugin submission guidelines and the review process lack the scrutiny that would otherwise be funded by a commission-based app platform. And many of these free plugins that are on the directory are limited versions of fuller featured, more premium plugins that are sold on third party code-markets or individual websites. And these third-party markets require absolutely no code review. So, you might pick up a plugin from the WordPress Plugin Directory, find out you need a special feature that is only available in the paid version, and pay for it from a third-party market, where the full featured plugin has more security holes than Swiss cheese.</p><p data-rte-preserve-empty="true" class=""></p>





















  
  














































  

    
  
    

      

      
        <figure class="
              sqs-block-image-figure
              intrinsic
            "
        >
          
        
        

        
          
            
          
            
                
                
                
                
                
                
                
                <img data-stretch="false" data-image="https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/f2aa0eec-93fe-41d6-8e93-0b081cc8030b/black-board-bright-226569.jpg" data-image-dimensions="3264x2448" data-image-focal-point="0.5,0.5" alt="" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/f2aa0eec-93fe-41d6-8e93-0b081cc8030b/black-board-bright-226569.jpg?format=1000w" width="3264" height="2448" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" onload="this.classList.add(&quot;loaded&quot;)" srcset="https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/f2aa0eec-93fe-41d6-8e93-0b081cc8030b/black-board-bright-226569.jpg?format=100w 100w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/f2aa0eec-93fe-41d6-8e93-0b081cc8030b/black-board-bright-226569.jpg?format=300w 300w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/f2aa0eec-93fe-41d6-8e93-0b081cc8030b/black-board-bright-226569.jpg?format=500w 500w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/f2aa0eec-93fe-41d6-8e93-0b081cc8030b/black-board-bright-226569.jpg?format=750w 750w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/f2aa0eec-93fe-41d6-8e93-0b081cc8030b/black-board-bright-226569.jpg?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/f2aa0eec-93fe-41d6-8e93-0b081cc8030b/black-board-bright-226569.jpg?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/f2aa0eec-93fe-41d6-8e93-0b081cc8030b/black-board-bright-226569.jpg?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs">

            
          
        
          
        

        
      
        </figure>
      

    
  


  





  <h3><strong>REASON 4 - WordPress Paints a Bullseye on Your Website</strong></h3><p class="">It's really easy to determine if your website is built with WordPress. In fact, most of the time it's announced in the footer of your site. This is why WordPress sites get over <a href="https://blog.akismet.com/2015/05/01/april-2015-stats-roundup/" target="_blank">132 million spam messages every month</a>. The number of spam comments on a WordPress site is 24 times higher than the number of legitimate comments. If it's that easy for spammers to know your site is WordPress, it's just that easy for malicious bots to find out as well. As it turns out, the majority of WordPress websites are hacked by bots. So you're not really being targeted because of something you said, or because of your great business success. You're WordPress site is being targeted by hackers because it is WordPress.</p><p data-rte-preserve-empty="true" class=""></p><h3><strong>REASON 5 - WordPress Requires Relatively Complex Systems</strong></h3><p class="">I personally don't know how we got to this place on the Internet. Why have so many businesses and individuals gone out of their way to get hosting on a Linux server with Apache, MySQL, PHP, and a complex CMS for 5 pages or less of content that never changes? I am aware that the reason boils down to simplicity. Anyone can install a WordPress site on the cheapest web hosting provider with a single click. But the reality is that it's overkill. <a href="https://www.openhub.net/p/wordpress/analyses/latest/languages_summary" target="_blank">WordPress has 1,326,910 lines of code</a>. All of that code is going to generate essentially about 1000 lines of HTML and JavaScript when it's all said and done. Adding all of that complexity opens up more points for hackers to attack your website. For example, if you used WordPress to make a 5 page website, you will be giving hackers the ability to try attacks on potential vulnerabilities within wordpress's 2159+ files and if a hackers gets through one of those vulnerabilities it opens the gateway for them to try attacks on potential vulnerabilities in PHP, Apache, MySQL and the OS your server is running. If you just had a simple 5-page HTML website, you reduce those points of entry to 5 and remove the need for PHP or MySQL.</p><p data-rte-preserve-empty="true" class=""></p>





















  
  














































  

    
  
    

      

      
        <figure class="
              sqs-block-image-figure
              intrinsic
            "
        >
          
        
        

        
          
            
          
            
                
                
                
                
                
                
                
                <img data-stretch="false" data-image="https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/6affff48-043c-40de-a4d7-db684e2e5f9e/jeshoots-com-523925-unsplash.jpg" data-image-dimensions="5472x3648" data-image-focal-point="0.5,0.5" alt="" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/6affff48-043c-40de-a4d7-db684e2e5f9e/jeshoots-com-523925-unsplash.jpg?format=1000w" width="5472" height="3648" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" onload="this.classList.add(&quot;loaded&quot;)" srcset="https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/6affff48-043c-40de-a4d7-db684e2e5f9e/jeshoots-com-523925-unsplash.jpg?format=100w 100w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/6affff48-043c-40de-a4d7-db684e2e5f9e/jeshoots-com-523925-unsplash.jpg?format=300w 300w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/6affff48-043c-40de-a4d7-db684e2e5f9e/jeshoots-com-523925-unsplash.jpg?format=500w 500w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/6affff48-043c-40de-a4d7-db684e2e5f9e/jeshoots-com-523925-unsplash.jpg?format=750w 750w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/6affff48-043c-40de-a4d7-db684e2e5f9e/jeshoots-com-523925-unsplash.jpg?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/6affff48-043c-40de-a4d7-db684e2e5f9e/jeshoots-com-523925-unsplash.jpg?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/6affff48-043c-40de-a4d7-db684e2e5f9e/jeshoots-com-523925-unsplash.jpg?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs">

            
          
        
          
        

        
      
        </figure>
      

    
  


  





  <h3><strong>If WordPress is That Risky, Then What Should I Use Instead?</strong></h3><p class="">If you're a small business I really would encourage you to use a hosted CMS solution instead of a self-hosted system. If you're not doing anything that's complex (such as a regular website or an online store) then I would highly recommend using Wix, Squarespace, Webflow, or Shopify. For a low monthly fee, they handle the hosting, security, design and development and all you have to do is write in your content and upload your products. Those sites are too generic looking you say? Then hire a web agency that specializes in headless websites that use Hugo, Jekyll or VuePress. While not as inexpensive as any of the above mentioned hosted CMSs you'll get exactly what you want. Need to have your cake and eat it too? I understand that WordPress is very easy and familiar, for some, and the time and cost to learn a new CMS isn't worth it. There is a last resort option for running your own WordPress, and that's called decoupling. WordPress can be used as a back end to power a static front end. You will once again need a web developer to do this, but if it's critical you need both self-hosted WordPress and security, then decoupling is your best option.</p>]]></content:encoded><media:content type="image/jpeg" url="https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/1712948859550-3MD0AFL65OIZ9QWLL2DT/wordpress-hacker.jpg?format=1500w" medium="image" isDefault="true" width="1500" height="1000"><media:title type="plain">5 Important Reasons Why You Should Not Use WordPress for Your Website</media:title></media:content></item><item><title>Trials and Tribulations of AWS EventBridge</title><dc:creator>Todd Willsie</dc:creator><pubDate>Thu, 27 Apr 2023 19:10:00 +0000</pubDate><link>https://www.jot.digital/blog/trials-and-tribulations-of-aws-eventbridge</link><guid isPermaLink="false">647fb19e93051a0534471f45:66215170b81b27326eed8167:66216cee2aef730914f606b9</guid><description><![CDATA[EventBridge is the AWS service that allows you to tap into the stream of 
events taking place within your AWS account. It is the most powerful 
service within AWS for monitoring your cloud environment but is generally 
an afterthought as compared to CloudTrail and CloudWatch. It was once apart 
of the CloudWatch suite of features but was broken off as a standalone 
service and expanded to form its own path as a multi-purpose event 
streaming service.]]></description><content:encoded><![CDATA[<p class="">EventBridge is the AWS service that allows you to tap into the stream of events taking place within your AWS account. It is the most powerful service within AWS for monitoring your cloud environment but is generally an afterthought as compared to CloudTrail and CloudWatch. It was once apart of the CloudWatch suite of features but was broken off as a standalone service and expanded to form its own path as a multi-purpose event streaming service.</p><p class="">Unlike CloudTail and CloudWatch Logs, EventBridge events are not captured by default and there isn’t an easy way to get a glimpse at the stream of events as they pass. If you fail to capture the events you desire, they will be gone for good.</p><p class="">Things have improved since the CloudWatch days. You can now create an archive of events and play them back for event rule development. They have also made many improvements in event rule functionality. But there are still some gotchas that will trip you up. Here are some things that have tripped me up.</p><h3><strong>Silent Failures</strong></h3><p class="">If there is a problem with an event rule, the event rule will fail its invocation. This is a problem as there isn’t any logging as to what the issue would be. The only evidence of these invocation failures is a metric recorded in CloudWatch and displayed under the event rule’s monitoring tab within the EventBridge interface. To be aware of these rule failures, you would have to setup a CloudWatch alarm. As for diagnosing the issue, if the problem is with the target, you will have to search through Lambda logs or CloudTrail for errors and hope you find something. If the issue is with the configuration of the rule and its pattern matching, you may end up with nothing to work with.</p><p class="">It is a good practice to create a CloudWatch metrics alarm to check for EventBridge invocation failures. This will give you some visibility of any event rule failures. For better visibility, you could setup a dead-letter queue on your event rules and have a Lambda function create log entries from that SQS queue in a CloudWatch Logs log group.</p><h3><strong>Event Patterns</strong></h3>





















  
  














































  

    
  
    

      

      
        <figure class="
              sqs-block-image-figure
              intrinsic
            "
        >
          
        
        

        
          
            
          
            
                
                
                
                
                
                
                
                <img data-stretch="false" data-image="https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/d2ae018c-a237-438b-a674-d127ac60d934/pattern-matching.png" data-image-dimensions="560x675" data-image-focal-point="0.5,0.5" alt="" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/d2ae018c-a237-438b-a674-d127ac60d934/pattern-matching.png?format=1000w" width="560" height="675" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" onload="this.classList.add(&quot;loaded&quot;)" srcset="https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/d2ae018c-a237-438b-a674-d127ac60d934/pattern-matching.png?format=100w 100w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/d2ae018c-a237-438b-a674-d127ac60d934/pattern-matching.png?format=300w 300w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/d2ae018c-a237-438b-a674-d127ac60d934/pattern-matching.png?format=500w 500w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/d2ae018c-a237-438b-a674-d127ac60d934/pattern-matching.png?format=750w 750w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/d2ae018c-a237-438b-a674-d127ac60d934/pattern-matching.png?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/d2ae018c-a237-438b-a674-d127ac60d934/pattern-matching.png?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/d2ae018c-a237-438b-a674-d127ac60d934/pattern-matching.png?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs">

            
          
        
          
        

        
      
        </figure>
      

    
  


  





  <h4><strong>Pattern Content Filters</strong></h4><p class="">The recent addition of content filters adds some much-needed flexibility to the pattern matching. I would like to highlight a feature called multiple matching. AWS’ documentation does touch on <a href="https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-event-patterns-content-based-filtering.html#eb-filtering-complex-example">multiple matching</a>, but only in relation to having a single content filter on multiple keys (properties). It is not possible to match multiple content filters on the same key, such as matching a prefix and a suffix. Attempting to use the same key more than once will cause only the last one to use evaluated. They now have a note mentioning this behavior. For the moment, there is no way to perform an <em>all-of</em> match against a single key. The reason for this might be down to how content filters are not delineated from regular key names and could cause conflicts. I suspect the EventBridge team have backed themselves into a corner with this design.</p><p class=""><strong>NOTE: The following examples do not work</strong></p><p class="">This will return the error ‘<em>Event pattern is not valid. Reason: “prefix” must be an object or an array</em>’.</p>





















  
  



<pre class="source-code">&quot;object&quot;: {
  &quot;key&quot;: {
    &quot;suffix&quot;: &quot;.csv&quot;,
    &quot;prefix&quot;: &quot;folder/&quot;
  }
}</pre>


  <p class="">This will treat prefix and suffix as keys and always fail to match.</p>





















  
  



<pre class="source-code">&quot;object&quot;: {
  &quot;key&quot;: {
    &quot;suffix&quot;: [&quot;.csv&quot;],
    &quot;prefix&quot;: [&quot;folder/&quot;]
  }
}</pre>


  <p class="">It is also not possible to pass a list of values to a content filter.</p>





















  
  



<pre class="source-code">&quot;object&quot;: {
  &quot;key&quot;: [
    {
      &quot;suffix&quot;: [ &quot;.csv&quot;, &quot;.json&quot;]
    }
  ]
}</pre>


  <p class="">With the recent addition of the “$or” functionality, which uses a “$” character to delineate it from key names. This deviates greatly from all other event pattern functionality and show a re-think on how advanced functionality is defined. We will likely see future content filters and other features being prefixed with ‘$’. There is no way to fix the current content filters without breaking backwards compatibility. They will need to release a version 2 for event pattern syntax.</p><p class="">The good news is that it is possible to perform an any-of match on a list of content filters for a single key. This doesn’t help us with the use case above, but does allow for some other ones that are beneficial.</p><p class="">The following will match on the suffix or prefix, but not both:</p>





















  
  



<pre class="source-code">&quot;object&quot;: {
  &quot;key&quot;: [
    {
      &quot;suffix&quot;: &quot;.csv&quot;
    },
    {
      &quot;prefix&quot;: &quot;folder/&quot;
    }
  ]
}</pre>


  <p class="">You can duplicate content filters, unlike keys. This now allows us to match both suffixes:</p>





















  
  



<pre class="source-code">&quot;object&quot;: {
  &quot;key&quot;: [
    {
      &quot;suffix&quot;: &quot;.csv&quot;
    },
    {
      &quot;suffix&quot;: &quot;.json&quot;
    }
  ]
}</pre>


  <p class="">The AWS documentation never gives an example of using more then a single content filter object within a key’s value list. You can actually specify multiple content filter objects and even mix them with plain text values. As long as one of the content filters or text values matches, the event will match the pattern.</p><p class="">This undocumented ability of multiple-matching is inherited from the base functionality where providing a list of text values within [] brackets will default to match any-of instead of all-of. If the event property matched one of the items in the key’s value list, the event will match the pattern. The fact that content filter are treated the same as text values when it comes to potential values is a hidden bonus.</p><p class="">This discovery of using multiple content-based filters on a single key fixed an edge case issue that was causing me to give up on complex pattern matching within the event rules and re-create my own pattern matching engine in a Lambda function. The following example is how you can match a path that may not always exist.</p><p class="">Let’s say you have a specific “deployment-role” IAM role whose events you want to ignore. Well, you would use an “anything-but” content filter to exclude it. What happens if you have an AWS account with IAM users that don’t assume roles? Well, the events caused by an IAM user will not contain the same “sessionIssuer” section as an event caused by someone assuming an IAM role. If the path does not exist, “anything-but” will fail to match events caused by the IAM user. A value at the specified path is required to be there to have that content filter match. Strangely, when one of those events occur, the event rule will not just skip the event, but will actually fail the invocation.</p><p class="">To solve this problem, you can use multiple matching and add an {“exists”: false} content filter into the same list as the “anything-but” content filter. As long as one of those content filters is true, the event will match the pattern.</p>





















  
  



<pre class="source-code">{
  &quot;detail-type&quot;: [&quot;AWS API Call via CloudTrail&quot;],
  &quot;source&quot;: [&quot;aws.ec2&quot;],
  &quot;detail&quot;: {
    &quot;eventName&quot;: [&quot;CreateSecurityGroup&quot;, &quot;DeleteSecurityGroup&quot;, &quot;AuthorizeSecurityGroupIngress&quot;, &quot;AuthorizeSecurityGroupEgress&quot;, &quot;RevokeSecurityGroupIngress&quot;, &quot;RevokeSecurityGroupEgress&quot;],
    &quot;eventSource&quot;: [&quot;ec2.amazonaws.com&quot;],
    &quot;userIdentity&quot;: {
      &quot;sessionContext&quot;: {
        &quot;sessionIssuer&quot;: {
          &quot;userName&quot;: [
            {
              &quot;anything-but&quot;: [&quot;deployment-role&quot;]
            },
            {
              &quot;exists&quot;: false
            }
          ]
        }
      }
    }
  }
}</pre>


  <p class="">When an IAM user causes an event, the username property is found under “sessionContext”. The “sessionContext/sessionIssuer” section is empty. When an IAM role causes an event, the username property is found in “sessionContext/sessionIssuer”. This highlights how not just cloudTrail events, but many other AWS events do not adhere to a strict structure. Properties will move around and identifiers will change. Another example of this is where an iam:CreatePolicy event will return a policyName attribute while an iam:DeletePolicy and all other policy events will return a policyArn attribute instead. You might think the trick above could solve this issue, but it can’t. You could potentially match events where neither username paths exist. This brings us to another limitation with event patterns, there isn’t support for conditionals or string manipulation to solve these complex but common situations. If you want to consistently extract certain details without running into invocation failures, you will need to send the event to a Lambda function for pre-processing and transformation.</p><h4><strong>Events With Lists</strong></h4><p class="">Occasionally you will run into an event with a list of objects. This is actually a common occurrence with CloudTrail as it will return a list of affected resources within the responseElements path. Let’s say you want to match if a specific resource has been affected. You would expect to be able to do a matching of an attribute with the object of that resource list. Well, it is not possible to do that. You are not able to drill into a list of objects to do a match for a specific object. With event rule patterns, once you run into a list, you can’t go any further.</p><h3><strong>Input Transformer</strong></h3>





















  
  














































  

    
  
    

      

      
        <figure class="
              sqs-block-image-figure
              intrinsic
            "
        >
          
        
        

        
          
            
          
            
                
                
                
                
                
                
                
                <img data-stretch="false" data-image="https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/78a5860e-5644-4d92-99f2-0ee1722c69c9/input-transformer.png" data-image-dimensions="3335x1876" data-image-focal-point="0.5,0.5" alt="" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/78a5860e-5644-4d92-99f2-0ee1722c69c9/input-transformer.png?format=1000w" width="3335" height="1876" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" onload="this.classList.add(&quot;loaded&quot;)" srcset="https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/78a5860e-5644-4d92-99f2-0ee1722c69c9/input-transformer.png?format=100w 100w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/78a5860e-5644-4d92-99f2-0ee1722c69c9/input-transformer.png?format=300w 300w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/78a5860e-5644-4d92-99f2-0ee1722c69c9/input-transformer.png?format=500w 500w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/78a5860e-5644-4d92-99f2-0ee1722c69c9/input-transformer.png?format=750w 750w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/78a5860e-5644-4d92-99f2-0ee1722c69c9/input-transformer.png?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/78a5860e-5644-4d92-99f2-0ee1722c69c9/input-transformer.png?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/78a5860e-5644-4d92-99f2-0ee1722c69c9/input-transformer.png?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs">

            
          
        
          
        

        
      
        </figure>
      

    
  


  





  <h4><strong>Illegal paths</strong></h4><p class="">Like I mentioned above, events do not follow a strict structure. Some events will contain properties that others will not. Sometimes an AWS service will return different identifiers depending on the event. An example of this is when one event returns an ARN while the other returns a resource name.</p><p class="">These differences between AWS events structures are little nightmares when working with the Input Transformer. Input Transformer requires that all paths, specified in the input paths JSON, exist. If any of them do not, your event rule invocation will fail, silently. This is where a <em>coalesce</em> style function or conditionals would be useful. As done with event patterns, adding functions to JSON based templates is difficult, but not impossible. Hopefully there are solutions in the future for allowing missing paths or pulling a value from the first available path (coalesce).</p><p class="">To get around this, you would have to create highly specific event rules that match each type of event you would want to react to, pass whole sections of the event through or pass the unaltered event to something else to process. Doing this may cause you to sometimes run into the dynamic data limit mentioned below. This only leaves you with option of sending the full event to a Lambda function for pre-processing and transformation.</p><h4><strong>Dynamic Data</strong></h4><p class="">Some data will cause the input transformer to break. An example is an embedded policy document which happens on IAM policy change events. When modifying an IAM policy, a copy of the document is included within the requestParameters and/or responseElements sections. If you try to pass in the full event or section containing the document as a property, it will cause the invocation to fail. The AWS documentation lists a value length limit of 256 characters, but that doesn’t appear to be the same as the computed value length limit, as I have far exceeded that without issue. There is either a special character or a content length issue. I am not sure as I have no error message to work with, only theories. As you are not able to manipulate the JSON object within the input paths, you will be forced to give up on the import transformer and send the unaltered matched event to a Lambda function for pre-processing and transformation. Are you seeing a pattern here?</p><h4><strong>Matched Event</strong></h4><p class="">If you do away with the Input Transformer and go back to using matched events, your Lambda function target will have to rely on the contents of the original event to identify and direct it to its final destination. With matched event, you will not have the luxury of a rule name, additional attributes, or tags to help you. This breaks the practice of using a generic Lambda function for processing events from multiple event rules and sources. You may need to create separate Lambda functions to handle events from every AWS service you work with and possibly every destination.</p><p class="">Let’s say you want code pipeline events to go to the developers, EC2 and ECS events to go to the cloud infrastructure team, or VPC changes to the security team. You would need to have separate Lambda processing functions for each event rule or build a single complex Lambda function that uses a lookup table (DynamoDB) to help identity the event, transform it, determine which team the event message should be sent to, and how the message should look. If you get to this point, you have effectively recreated EventBridge’s event rules with the functionality you needed.</p>





















  
  














































  

    
  
    

      

      
        <figure class="
              sqs-block-image-figure
              intrinsic
            "
        >
          
        
        

        
          
            
          
            
                
                
                
                
                
                
                
                <img data-stretch="false" data-image="https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/1728b170-3061-493f-98df-94a9a86b0b15/eventual-consistency.jpg" data-image-dimensions="1024x681" data-image-focal-point="0.5,0.5" alt="" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/1728b170-3061-493f-98df-94a9a86b0b15/eventual-consistency.jpg?format=1000w" width="1024" height="681" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" onload="this.classList.add(&quot;loaded&quot;)" srcset="https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/1728b170-3061-493f-98df-94a9a86b0b15/eventual-consistency.jpg?format=100w 100w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/1728b170-3061-493f-98df-94a9a86b0b15/eventual-consistency.jpg?format=300w 300w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/1728b170-3061-493f-98df-94a9a86b0b15/eventual-consistency.jpg?format=500w 500w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/1728b170-3061-493f-98df-94a9a86b0b15/eventual-consistency.jpg?format=750w 750w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/1728b170-3061-493f-98df-94a9a86b0b15/eventual-consistency.jpg?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/1728b170-3061-493f-98df-94a9a86b0b15/eventual-consistency.jpg?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/1728b170-3061-493f-98df-94a9a86b0b15/eventual-consistency.jpg?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs">

            
          
        
          
        

        
      
        </figure>
      

    
  


  





  <h3><strong>Eventual Consistency</strong></h3><p class="">This isn’t as much of an issue as it is a fact of life. Your event rule may not fire when the event actually happens. Sometime AWS has issues where the event bus stream is delayed. In those cases, the events will eventually get posted to the bus and your event rule could trigger many hours after when it should have. There is also a chance an event could be duplicated, or your rule gets triggered twice. You may need to think of these edge cases when planning your event rules. If you have a rule starting an EC2 instance or ECS task on a matching event, you could end up with a bit of a mess.</p><p class="">In addition to event triggered rules, classic schedule event rules are also affected by EventBridge’s eventual consistency, and your rules may not trigger at the time you expected.</p><p class="">They have recently added new functionality under the rule trigger configuration to specify a shorter retry window and the maximum number of retry attempts. They also have added support for a dead-letter queue for collecting failed invocations.</p><h3><strong>Scheduler</strong></h3><p class="">AWS has built a new EventBridge Scheduler to replace the scheduled event rules and centralize scheduling of events. It is a completely separate service under the hood with its own API interface and quotas, but officially it is still housed under the EventBridge umbrella. It has the same new features mentioned above that help deal with eventual consistency. In addition, there is also support for a flexible start window, time-zones, and grouping.</p><h3><strong>Troubleshooting</strong></h3><p class="">Previously, troubleshooting event rule issues was nearly impossible as there wasn’t any event history to work with, unlike CloudTrail. If you wanted to get a snapshot of the events you were trying to match, you had to make an event rule with a generic pattern and target a Lambda function to dump the event details to a CloudWatch log group. The new archive/replay functionality of EventBridge doesn’t provide any visibility, but it does allow you to replay those captured events from within EventBridge. Having a list of captured events is useful for testing event rules, but it doesn’t make troubleshooting effortless as not all causes of a failed event rule invocation will produce an error event in CloudTrail. The recent addition of the dead-letter queue may help a bit more with troubleshooting. There still isn’t a single pain of glass to help you with troubleshooting event rule issues. You will need to build your own monitoring and perform a lot of trial-and-error to solve invocation issues.</p><p class=""><strong>Pro tip:</strong> One of the first event rules you should create is one that alerts you on event rule invocation failures. Without custom alerting there is no way to monitor that your event rules are working without checking CloudWatch metrics and CloudTrail logs.</p><h3><strong>Conclusion</strong></h3><p class="">For many years, EventBridge was another section under CloudWatch and its pattern matching engine was very basic to not that effective. Since the split, they have added many new features to the EventBridge service such as streams, archive/replay, schema registry, and pipes. In addition to those features there were also a lot of improvements made to the pattern matching engine and expanding the list of trigger destinations. Some recent changes have allowed me to strike off some of the issues on my list, but, as outlined above, there is still room for improvement when it comes to debugging, error handling, pattern matching, and the input transformer.</p>]]></content:encoded><media:content type="image/jpeg" url="https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/1713467466126-WJ8T0JONFN02QSXM9MYY/Trials-and-Tribulations.jpg?format=1500w" medium="image" isDefault="true" width="650" height="366"><media:title type="plain">Trials and Tribulations of AWS EventBridge</media:title></media:content></item><item><title>Azure Landing Zones and Networking</title><dc:creator>Todd Willsie</dc:creator><pubDate>Thu, 26 Jan 2023 21:42:00 +0000</pubDate><link>https://www.jot.digital/blog/azure-landing-zones-and-networking</link><guid isPermaLink="false">647fb19e93051a0534471f45:66215170b81b27326eed8167:662185109c49e73790095b44</guid><description><![CDATA[Networking is simple, right? Sure thing boss! No, seriously, this blog post 
could be a book. In order to keep it focused, I want to concentrate on 
networking within Azure. An Azure Virtual Network (VNet) is the primary 
building block for creating your private network in Azure. Typically, 
deployments will have a few to several virtual networks, providing 
management and networking boundaries between workloads.]]></description><content:encoded><![CDATA[<p class="">I’m continuing the series on Azure Landing Zones (ALZ). If you want to catch up, here are links to the previous entries:</p><ul data-rte-list="default"><li><p class=""><a href="https://www.jot.digital/blog/what-is-an-azure-landing-zone" target="_blank">What is an Azure Landing Zone?</a></p></li><li><p class=""><a href="https://www.keepsecure.ca/blog/azure_landing_zones_and_break_glass_accounts/">Azure Landing Zones and Break-glass Accounts</a></p></li></ul><p class="">In this post, let’s talk about VNet networking within Azure Landing Zones.</p><h2><strong>Networking is simple, right?</strong></h2><p class="">Sure thing boss! No, seriously, this blog post could be a book. In order to keep it focused, I want to concentrate on networking within Azure. An <a href="https://learn.microsoft.com/en-us/azure/virtual-network/virtual-networks-overview">Azure Virtual Network</a> (VNet) is the primary building block for creating your private network in Azure. Typically, deployments will have a few to several virtual networks, providing management and networking boundaries between workloads.</p><p class="">In order to understand the topologies within the Azure Landing Zone, we need to cover a couple of concepts.</p><h3><strong>Peering</strong></h3><p class="">Virtual networks are private address spaces that can be used to deploy resources. Virtual networks can then be connected by way of peering. With peering, resources in separate virtual networks can directly address each other using private IP addresses. Importantly, Azure supports the concept of global peering, which allows you to directly connect two virtual networks in different regions to each other.</p><p class="">Peering is a complicated topic, but one point to understand for this discussion is the concept of VNet transit. In Azure, virtual networks that are peered together can talk to each other directly. However, spokes in a traditional hub-and-spoke topology cannot natively talk to each other. A networking device (router/firewall/etc.) is required to allow this traffic to work.</p><h3><strong>Azure Firewall</strong></h3><p class="">According to the <a href="https://learn.microsoft.com/en-us/azure/firewall/overview">docs</a> Azure Firewall is a “cloud-native and intelligent network firewall service”. As opposed to <a href="https://azure.microsoft.com/en-us/solutions/network-appliances/">network virtual appliances</a> (NVAs) you can think of Azure Firewall as a “firewall-as-a-service”. Designed to scale and providing many of the same features as traditional NVAs, Azure Firewall can be an effective way of providing both north-south (in/out of your environment) and east-west (lateral between systems/services in your environment) protection for your Azure workloads.</p><h3><strong>Network Security Groups</strong></h3><p class=""><a href="https://learn.microsoft.com/en-us/azure/virtual-network/network-security-groups-overview">Network Security Groups</a> allow you to filter traffic at the network level for resources deployed in Azure. Configurable on both the subnet and resource level, you can make use of network security groups to provide micro-segmentation protection.</p><h2><strong>Networking Options</strong></h2><p class="">The ALZ has two built-in models for networking in enterprise scale.</p><h3><strong>Hub-And-Spoke</strong></h3><p class="">The traditional approach to Azure networking is the create a <a href="https://github.com/Azure/Enterprise-Scale/blob/main/docs/reference/adventureworks/README.md">hub-and-spoke topology</a>.</p>





















  
  














































  

    
  
    

      

      
        <figure class="
              sqs-block-image-figure
              intrinsic
            "
        >
          
        
        

        
          
            
          
            
                
                
                
                
                
                
                
                <img data-stretch="false" data-image="https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/6c96f57c-eee9-4d9c-a392-2bc62c6c1a17/hub-spoke.png" data-image-dimensions="1093x682" data-image-focal-point="0.5,0.5" alt="" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/6c96f57c-eee9-4d9c-a392-2bc62c6c1a17/hub-spoke.png?format=1000w" width="1093" height="682" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" onload="this.classList.add(&quot;loaded&quot;)" srcset="https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/6c96f57c-eee9-4d9c-a392-2bc62c6c1a17/hub-spoke.png?format=100w 100w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/6c96f57c-eee9-4d9c-a392-2bc62c6c1a17/hub-spoke.png?format=300w 300w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/6c96f57c-eee9-4d9c-a392-2bc62c6c1a17/hub-spoke.png?format=500w 500w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/6c96f57c-eee9-4d9c-a392-2bc62c6c1a17/hub-spoke.png?format=750w 750w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/6c96f57c-eee9-4d9c-a392-2bc62c6c1a17/hub-spoke.png?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/6c96f57c-eee9-4d9c-a392-2bc62c6c1a17/hub-spoke.png?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/6c96f57c-eee9-4d9c-a392-2bc62c6c1a17/hub-spoke.png?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs">

            
          
        
          
        

        
      
        </figure>
      

    
  


  





  <p class="">Remember, spokes cannot talk to each other. So, in this topology, a firewall or NVA is required in the hub to enable that communication flow. Because of this, all communication flows will require some step for configuration of that firewall.</p><p class="">One consideration is how to expand this networking topology to be cross region. There are likely 2 ways to accomplish this. The first way would be to create another hub/spoke topology in the second region, and then peer the two “hubs” together. In this scenario, the firewall would be traversed twice during networking transit between regions.</p>





















  
  














































  

    
  
    

      

      
        <figure class="
              sqs-block-image-figure
              intrinsic
            "
        >
          
        
        

        
          
            
          
            
                
                
                
                
                
                
                
                <img data-stretch="false" data-image="https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/419f2ab6-914b-46d8-ac30-c18afc31abd5/hub-spoke+%281%29.png" data-image-dimensions="610x322" data-image-focal-point="0.5,0.5" alt="" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/419f2ab6-914b-46d8-ac30-c18afc31abd5/hub-spoke+%281%29.png?format=1000w" width="610" height="322" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" onload="this.classList.add(&quot;loaded&quot;)" srcset="https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/419f2ab6-914b-46d8-ac30-c18afc31abd5/hub-spoke+%281%29.png?format=100w 100w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/419f2ab6-914b-46d8-ac30-c18afc31abd5/hub-spoke+%281%29.png?format=300w 300w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/419f2ab6-914b-46d8-ac30-c18afc31abd5/hub-spoke+%281%29.png?format=500w 500w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/419f2ab6-914b-46d8-ac30-c18afc31abd5/hub-spoke+%281%29.png?format=750w 750w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/419f2ab6-914b-46d8-ac30-c18afc31abd5/hub-spoke+%281%29.png?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/419f2ab6-914b-46d8-ac30-c18afc31abd5/hub-spoke+%281%29.png?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/419f2ab6-914b-46d8-ac30-c18afc31abd5/hub-spoke+%281%29.png?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs">

            
          
        
          
        

        
      
        </figure>
      

    
  


  





  <p class="">The second way would be to globally peer VNets together that are allowed to talk to each other.</p>





















  
  














































  

    
  
    

      

      
        <figure class="
              sqs-block-image-figure
              intrinsic
            "
        >
          
        
        

        
          
            
          
            
                
                
                
                
                
                
                
                <img data-stretch="false" data-image="https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/688cd6b4-5cb8-4978-82d6-f449424a5153/global-peer.png" data-image-dimensions="584x288" data-image-focal-point="0.5,0.5" alt="" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/688cd6b4-5cb8-4978-82d6-f449424a5153/global-peer.png?format=1000w" width="584" height="288" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" onload="this.classList.add(&quot;loaded&quot;)" srcset="https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/688cd6b4-5cb8-4978-82d6-f449424a5153/global-peer.png?format=100w 100w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/688cd6b4-5cb8-4978-82d6-f449424a5153/global-peer.png?format=300w 300w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/688cd6b4-5cb8-4978-82d6-f449424a5153/global-peer.png?format=500w 500w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/688cd6b4-5cb8-4978-82d6-f449424a5153/global-peer.png?format=750w 750w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/688cd6b4-5cb8-4978-82d6-f449424a5153/global-peer.png?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/688cd6b4-5cb8-4978-82d6-f449424a5153/global-peer.png?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/688cd6b4-5cb8-4978-82d6-f449424a5153/global-peer.png?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs">

            
          
        
          
        

        
      
        </figure>
      

    
  


  





  <h3><strong>Virtual WAN</strong></h3><p class=""><a href="https://learn.microsoft.com/en-us/azure/virtual-wan/virtual-wan-about">Azure Virtual WAN</a> is a “meta” networking service within Azure that brings together several services in Azure to greatly decrease the complexity of networking. You can see how the ALZ uses Virtual WAN <a href="https://github.com/Azure/Enterprise-Scale/blob/main/docs/reference/contoso/Readme.md">here</a>.</p><p class="">In this setup, you effectively deploy a Virtual WAN Hub in each region. Hubs can connect to each other directly. Virtual networks can associate themselves with the hub. Virtual WAN offers a couple of cool capabilities such as direct VNet transit access and secure Virtual WAN hubs. I’m not going to go into detail here, but you can likely understand that there are several benefits to this topology over the traditional hub-and-spoke.</p><p class="">Lastly, you can enable <a href="https://learn.microsoft.com/en-us/azure/virtual-wan/virtual-wan-global-transit-network-architecture#anytoany">any-to-any type communication patterns</a>.</p>





















  
  














































  

    
  
    

      

      
        <figure class="
              sqs-block-image-figure
              intrinsic
            "
        >
          
        
        

        
          
            
          
            
                
                
                
                
                
                
                
                <img data-stretch="false" data-image="https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/2a93150b-5bb6-4ba3-a470-8ef1fb805256/figure4.png" data-image-dimensions="1069x778" data-image-focal-point="0.5,0.5" alt="" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/2a93150b-5bb6-4ba3-a470-8ef1fb805256/figure4.png?format=1000w" width="1069" height="778" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" onload="this.classList.add(&quot;loaded&quot;)" srcset="https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/2a93150b-5bb6-4ba3-a470-8ef1fb805256/figure4.png?format=100w 100w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/2a93150b-5bb6-4ba3-a470-8ef1fb805256/figure4.png?format=300w 300w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/2a93150b-5bb6-4ba3-a470-8ef1fb805256/figure4.png?format=500w 500w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/2a93150b-5bb6-4ba3-a470-8ef1fb805256/figure4.png?format=750w 750w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/2a93150b-5bb6-4ba3-a470-8ef1fb805256/figure4.png?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/2a93150b-5bb6-4ba3-a470-8ef1fb805256/figure4.png?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/2a93150b-5bb6-4ba3-a470-8ef1fb805256/figure4.png?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs">

            
          
        
          
        

        
      
        </figure>
      

    
  


  





  <h3><strong>Hidden 3rd Option</strong></h3><p class="">At time of writing, Azure has a new service called the <a href="https://learn.microsoft.com/en-us/azure/virtual-network-manager/overview">Azure Virtual Network Manager</a> (VNM). I feel like this service really combines the best of both the hub-and-spoke and the Virtual WAN solutions when it comes to VNet considerations within Azure. Here are some key features:</p><ul data-rte-list="default"><li><p class="">You can centrally manage policy which automatically creates your networking topology.</p></li><li><p class="">You can enable hub-and-spoke and mesh topologies (VNets with direct access to each other)</p></li><li><p class="">You can manage other features of your virtual networks, including network security groups and new <a href="https://learn.microsoft.com/en-us/azure/virtual-network-manager/concept-security-admins">admin rules</a></p></li></ul><p class="">There is currently no guidance on how to use ALZ with AVNM. For me, this option really allows you to start configuring your guardrails (around networking and network security) and enforcing that via policy within the platform. There are several reasons why I would recommend starting here vs the other two topologies mentioned above.</p><h2><strong>Conclusion</strong></h2><p class="">Networking is a complex topic with many nuances that can affect your security posture. I hope that I did an okay-ish job going over some of the basics here and showing that there are many alternatives to building a secure networking topology within Azure.</p>]]></content:encoded><media:content type="image/png" url="https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/1713472955213-D234NOK5ZRGE0OJ7W7QV/Microsoft_Azure.png?format=1500w" medium="image" isDefault="true" width="1200" height="1200"><media:title type="plain">Azure Landing Zones and Networking</media:title></media:content></item><item><title>Azure Landing Zones and Break-Glass Accounts</title><dc:creator>Todd Willsie</dc:creator><pubDate>Wed, 18 Jan 2023 21:50:00 +0000</pubDate><link>https://www.jot.digital/blog/azure-landing-zones-and-break-glass-accounts</link><guid isPermaLink="false">647fb19e93051a0534471f45:66215170b81b27326eed8167:662186d548041012ef965872</guid><description><![CDATA[Azure landing zones are a recommended way for deploying Azure topologies 
for both new and existing Azure customers. I wrote an intro blog post about 
Azure Landing Zones that you can check out. In this blog post, I’d like to 
examine integrating “break glass” accounts into a target architecture.]]></description><content:encoded><![CDATA[<p class="">Azure landing zones are a recommended way for deploying Azure topologies for both new and existing Azure customers. I wrote an intro blog post about Azure Landing Zones that you can <a href="https://www.jot.digital/blog/what-is-an-azure-landing-zone" target="">check o</a><a href="https://www.keepsecure.ca/blog/what_is_an_azure_landing_zone/">ut</a>. In this blog post, I’d like to examine integrating “break glass” accounts into a target architecture.</p><h2><strong>What are “break-glass” accounts?</strong></h2><p class="">First, some background. In Azure, user management is delegated off to a service called Azure Active Directory or AAD for short. AAD can have multiple different “user types”, but the most common are federated and cloud-only. In the federated case, identities can be enabled with multiple different authentication options, most of which effectively hand off the authentication responsibility to a 3rd party provider (that isn’t AAD). In the cloud-only case, authentication of user accounts is actually done by AAD itself. In all cases, user access can be protected with additional mechanisms such as Azure Multi-factor authentication (MFA) or Azure Conditional Access.</p><p class="">Typically, when enterprise customers set up access to Azure resources, they will do so by federating user accounts with their existing enterprise systems. This approach makes sense from a security perspective, as you can control how/when your identities are used centrally within your enterprise system. However, this architecture also introduces an extra failure case that you need to consider.</p><p class="">When you are planning your cloud deployment, you need to also plan for disaster recovery. Cloud consoles are typically accessible over the internet, and so you need to understand what your response would be in the following scenarios:</p><ul data-rte-list="default"><li><p class="">On-prem identity provider is offline, inaccessible, or having issues (with MFA, authorization, or other)</p></li><li><p class="">Azure MFA (the service) is having issues and not functioning correctly</p></li><li><p class="">Azure Conditional Access policies are not working correctly</p></li></ul><p class="">“Break glass” accounts are a strategy for providing access to the cloud console in the event of any of the failures described above. They are typically cloud-only accounts that do not have the additional security protections of MFA and Conditional Access. By creating accounts like this, you allow for management of your cloud resources in the event of failures in any of the above-described systems.</p><p class="">I think it is important to note that break-glass accounts are a tradeoff between continued operations and overall security. Typically, failure of authentication services does not prevent you from using already running cloud resources, but it does prevent you from managing them further. Businesses need to assess the tradeoff/risk of creating high-privileged accounts with limited account protections against the likelihood of failure in the authentication systems along with the impact. While Azure Landing Zones strongly recommend emergency access accounts, they might not always make sense for all situations.</p><h2><strong>Strategies for “break glass” accounts in Azure</strong></h2><p class="">To me, there are three different options when implementing “break glass” accounts.</p><h3><strong>AAD Global Admin Access Path</strong></h3><p class="">Within AAD there is a directory role called Global Admin. This role has full access to the directory. Further, global admins have the option to <a href="https://learn.microsoft.com/en-us/azure/role-based-access-control/elevate-access-global-admin">elevate access</a> to all Azure subscriptions. Effectively, using this function, you can become a <a href="https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles#user-access-administrator">User Access Administrator</a> at the root scope of your management group hierarchy. From there, you can make any access changes you need to make, including granting yourself more access.</p><h3><strong>EA “Account Administrator” Access Path</strong></h3><p class="">Depending on your agreement with Microsoft to purchase Azure, you may have what is called an <a href="https://learn.microsoft.com/en-us/azure/cost-management-billing/manage/ea-portal-agreements">Enterprise Agreement</a>. In this situation, billing is handled via a separate mechanism within Azure. This mechanism allows for the creation of enterprise enrollment accounts, and one of the roles that you create is an account administrator. All subscriptions will belong to exactly one enterprise enrollment account. Enterprise Enrollment account administrators can set the <a href="https://learn.microsoft.com/en-us/azure/cost-management-billing/manage/ea-portal-administration#create-a-subscription">service administrator for each subscription </a>. In this access path, your “break glass” account could be the enterprise enrollment account administrator. In the event of access issues, use the enterprise enrollment portal experience to set a different service administrator for the target subscription.</p><h3><strong>Management Group Access Path</strong></h3><p class="">Within Azure, all subscriptions belong to a hierarchy level within <a href="https://learn.microsoft.com/en-us/azure/governance/management-groups/overview">Azure Management Groups</a>. Even if you have never really configured this before, you always have the root management group where each subscription will automatically live. In this access path, your “break glass” accounts can be assigned a certain permission level (Owner, or other) within an appropriate area of your management group structure. If there are access issues, simply log in to Azure with your “break glass” account and use its permissions to fix any issues.</p><h2><strong>Recommendations?</strong></h2><p class="">Long story short, is, not really. All the access paths above have different trade-offs. Off the top of my head, here are some considerations:</p><ul data-rte-list="default"><li><p class="">Level of access of the “break glass” account, and what blast radius a compromise would mean</p></li><li><p class="">Organization structure for managing different areas of your Microsoft landscape</p></li><li><p class="">Ability to monitor/react to inappropriate use of break-glass accounts</p></li><li><p class="">Support model / structure</p></li></ul><p class="">Most conventional wisdom is to use the AAD global admin access path for your break-glass accounts. You can even see this recommendation for <a href="https://github.com/Azure/CanadaPubSecALZ/blob/main/docs/architecture.md#4-identity">Canadian Government Use</a>.</p><p class="">Hope you enjoyed the post!</p>]]></content:encoded><media:content type="image/png" url="https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/1713473061600-PZ8D1VI4YIUEK5GIYSQB/Microsoft_Azure.png?format=1500w" medium="image" isDefault="true" width="1200" height="1200"><media:title type="plain">Azure Landing Zones and Break-Glass Accounts</media:title></media:content></item><item><title>What is an Azure Landing Zone?</title><dc:creator>Todd Willsie</dc:creator><pubDate>Thu, 12 Jan 2023 21:44:00 +0000</pubDate><link>https://www.jot.digital/blog/what-is-an-azure-landing-zone</link><guid isPermaLink="false">647fb19e93051a0534471f45:66215170b81b27326eed8167:662185fb2af31f78d7245c91</guid><description><![CDATA[Recently I’ve been involved in a few client projects that are making use of 
Azure Landing Zones for their platform and application deployments. Landing 
zones are an interesting concept and so, in this blog post, let’s cover the 
basics.]]></description><content:encoded><![CDATA[<p class="">Recently I’ve been involved in a few client projects that are making use of <a href="https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/landing-zone/">Azure Landing Zones</a> for their platform and application deployments. Landing zones are an interesting concept and so, in this blog post, let’s cover the basics.</p><h2><strong>What is an Azure Landing Zone?</strong></h2><p class="">I think there are a couple of different ways to frame the concept of a landing zone.</p><p class="">From a <a href="https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/overview">cloud adoption framework</a> you can think of landing zones as the technical end-game. They represent the prescribed architecture that considers the people/process/technology guidance (as best as it can) laid out within the cloud adoption frameworks. Where required, assumptions are made about how the organization will want to interact with cloud resources, and those assumptions are baked into the deliverables.</p><p class="">The other way to frame landing zones is that they provide building blocks for organizations to use while adopting the cloud. The building blocks take into account Azure/product limitations along with a host of “best practices” as it relates to scaling, security, governance, networking, and identity. Organizations adopting the cloud can choose to use some or all of the prescribed components to build out their environment.</p><h2><strong>What is in the box?</strong></h2><p class="">I think it’s important to look at the design principles that went into creating the Azure Landing Zones.</p><h3><strong>Subscription Democratization</strong></h3><p class="">With this design principle, Azure Landing Zones utilize subscriptions as both a unit of scale and a as a unit of management. The idea is that centralized platform teams can create/govern core parts of a subscription (think networking, identity, governance via Azure Policy) but ultimately the management of a subscription belongs to the workload or business unit teams for which the subscription was created.</p><p class="">There are reasons why organizations would not find this a suitable approach to building out the cloud. One of the main reasons here is that it pushes various aspects of management (of cloud infrastructure) to teams that might not be well equipped to manage at that level. I would say that it takes more of a “DevOps approach” to cloud management rather than a “platform engineering” approach. Those teams are heavily overloaded, so, another way to think about it is that it forces the developers of an application to manage the infrastructure as well, rather than having a centralized team of cloud infra experts that work with development teams to create appropriate infra. Scaling, for example, is a far more nuanced topic than simply adding or removing subscriptions.</p><h3><strong>Policy-Driven Governance</strong></h3><p class="">In a policy-driven governance approach, cloud ops teams will make use of Azure Policy (and apply said policy) on top of democratized subscriptions. The idea is that the policy controls provide the guardrails by which Azure services can be used in the given context. Some of the Azure policies are easy enough to think about, such as ones that restrict the type of services that can be deployed and others that restrict the locations where services can be deployed.</p><p class="">There is a lot more to Azure policy than that, however. While Azure does release policy guidance (some which adhere to various security frameworks) that guidance is generic and requires tuning. This tuning goes beyond the traditional “exception” management. Many frameworks do not cover all services that Azure has, cannot deal with certain security controls, and do not reflect any corporate standards.</p><p class="">The key point here is that adopting Azure Landing Zones requires corporations to have proactive (rather than reactive) policy governance teams that are constantly evolving / testing the policy code to ensure compliance.</p><h3><strong>Simplified Management</strong></h3><p class="">The idea here is that the landing zones take into account best-practice patterns for usage of both infrastructure-as-a-service and platform-as-a-service tooling. This ensures that you are not restricted, at least initially, to service choice.</p><h3><strong>Application-Centric Service Model</strong></h3><p class="">The policy/governance tooling provided within the base landing zone configuration are designed to provide a set of services at the “application” level, not at the infrastructure component level. What this basically means is that you need to think about resources in terms of the application lifecycle. You’ll move away, for example, from deploying a centralized SQL server that hosts many application databases to creating application specific SQL servers. While this tradeoff may increase work for traditional infra teams (such as your DBAs) it’ll decrease work at the Azure policy/governance/management level.</p><h3><strong>Align with Azure-native design and roadmaps</strong></h3><p class="">Azure landing zones, obviously, make use of Azure-native services when addressing scale, security, governance, etc. When adopting Azure Landing Zones, you need to think about Azure services managing Azure, and then, from a corporate level, integrating reporting with other centralized systems.</p><h2><strong>Conclusion</strong></h2><p class="">I’ve never been a fan of generic advice as I feel like the whole role of architecture is to ensure that a solution is fit-for-purpose for a client environment and context. That being said, Azure Landing Zones seems like a great place to start for most organizations. I like that the landing zones are not just paper based, but there are ARM/BICEP/Terraform artifacts to deploy required components along with a CI/CD framework that seems easy enough to use. I’m hoping over the next few blog posts to dive into key areas within the Azure Landing Zone framework and discuss some possible architectural alternatives.</p>]]></content:encoded><media:content type="image/png" url="https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/1713473061600-PZ8D1VI4YIUEK5GIYSQB/Microsoft_Azure.png?format=1500w" medium="image" isDefault="true" width="1200" height="1200"><media:title type="plain">What is an Azure Landing Zone?</media:title></media:content></item><item><title>Deploying Azure Static Web Apps Manually</title><dc:creator>Todd Willsie</dc:creator><pubDate>Tue, 04 Jan 2022 21:37:00 +0000</pubDate><link>https://www.jot.digital/blog/deploying-azure-static-web-apps-manually</link><guid isPermaLink="false">647fb19e93051a0534471f45:66215170b81b27326eed8167:66218433e9d9a25420e628a4</guid><description><![CDATA[Azure Static Web Apps is a relatively new service that tries to streamline 
the process for building and deploying full stack web apps to Azure 
directly from a code repository. It effectively takes an opinionated view 
of how this type of development should be done.]]></description><content:encoded><![CDATA[<p class=""><a href="https://docs.microsoft.com/en-us/azure/static-web-apps/">Azure Static Web Apps</a> is a relatively new service that tries to streamline the process for building and deploying full stack web apps to Azure directly from a code repository. It effectively takes an opinionated view of how this type of development should be done and:</p><ul data-rte-list="default"><li><p class="">Creates a set of Azure resources (grouped into an Azure Static Web App) to host the code components</p></li><li><p class="">Creates/forces a set of CI/CD automation to support the deployment and promotion of code from a repository of your choice</p></li></ul><p class="">I’ve been using Azure Static Web Apps to host a few marketing type websites. It has been relatively quick and easy to setup. The one annoying thing, however, is that you have to have a CI/CD pipeline setup to perform a deployment. While this is always the recommended way, sometimes you just want to do it manually. You’ll notice that there are no tools provide by Azure to do this, so I had to go hunting around to make it work.</p><h3><strong>Investigating the GitHub Action</strong></h3><p class="">My first stop proved promising as I went to go and have a look at the github action that was created to support this deployment. Turns out it uses a docker container, which means we can try and reproduce the steps locally. Here is a link to the <a href="https://github.com/Azure/static-web-apps-deploy">repo</a></p><p class="">From here, you can effectively run the docker container and mount in your web artifacts using something like:</p>





















  
  



<pre class="source-code">docker run -it -v app:/app mcr.microsoft.com/appsvc/staticappsclient:stable /bin/bash</pre>


  <p class="">The main program to facilitate a deployment is located in the /bin/staticsites folder.</p><p class="">At first I thought I had to set a bunch of environment variables to make this work (as per the custom GitHub actions documentation) but it turns out that you can use –help on the StaticSitesClient binary to manually enter in the required information.</p><p class="">Here is a sample of the options for the “upload” action:</p>





















  
  














































  

    
  
    

      

      
        <figure class="
              sqs-block-image-figure
              intrinsic
            "
        >
          
        
        

        
          
            
          
            
                
                
                
                
                
                
                
                <img data-stretch="false" data-image="https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/f3532457-3486-4329-855b-893f85342952/static_web_app_help.png" data-image-dimensions="844x834" data-image-focal-point="0.5,0.5" alt="" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/f3532457-3486-4329-855b-893f85342952/static_web_app_help.png?format=1000w" width="844" height="834" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" onload="this.classList.add(&quot;loaded&quot;)" srcset="https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/f3532457-3486-4329-855b-893f85342952/static_web_app_help.png?format=100w 100w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/f3532457-3486-4329-855b-893f85342952/static_web_app_help.png?format=300w 300w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/f3532457-3486-4329-855b-893f85342952/static_web_app_help.png?format=500w 500w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/f3532457-3486-4329-855b-893f85342952/static_web_app_help.png?format=750w 750w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/f3532457-3486-4329-855b-893f85342952/static_web_app_help.png?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/f3532457-3486-4329-855b-893f85342952/static_web_app_help.png?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/f3532457-3486-4329-855b-893f85342952/static_web_app_help.png?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs">

            
          
        
          
        

        
      
        </figure>
      

    
  


  





  <p class="">Based on this, you can work out a command to deploy your relevant configuration. Keep in mind that you will need to specify your apiToken on the command line here.</p><p class="">Enjoy!</p>]]></content:encoded><media:content type="image/png" url="https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/1713472666756-OQMAGFRQ5G8Z5ESI9SKF/Microsoft_Azure.png?format=1500w" medium="image" isDefault="true" width="1200" height="1200"><media:title type="plain">Deploying Azure Static Web Apps Manually</media:title></media:content></item><item><title>Azure Storage Encryption At Rest</title><dc:creator>Todd Willsie</dc:creator><pubDate>Sat, 17 Oct 2020 19:44:00 +0000</pubDate><link>https://www.jot.digital/blog/azure-storage-encryption-at-rest</link><guid isPermaLink="false">647fb19e93051a0534471f45:66215170b81b27326eed8167:6621777de154cf28d8aa4f0a</guid><description><![CDATA[Azure storage encryption can be a pretty misunderstood concept and so the 
goal of this post is to provide an overview of the feature set and discuss 
an interesting preview that is currently in progress.]]></description><content:encoded><![CDATA[<p class="">zure storage encryption can be a pretty misunderstood concept and so the goal of this post is to provide an overview of the feature set and discuss an interesting preview that is currently in progress.</p><h3><strong>What is Azure Storage Encryption?</strong></h3><p class="">By default, all data stored in Azure storage accounts are encrypted at rest. This is done transparently at the storage service layer using a 256-bit AES Encryption key. The service and key usage is FIPS 140-2 compliant. As per the <a href="https://docs.microsoft.com/en-us/azure/storage/common/storage-service-encryption">documentation</a> this encryption is enabled automatically and cannot be disabled.</p><p class="">From a data flow perspective, it looks something like this:</p>





















  
  














































  

    
  
    

      

      
        <figure class="
              sqs-block-image-figure
              intrinsic
            "
        >
          
        
        

        
          
            
          
            
                
                
                
                
                
                
                
                <img data-stretch="false" data-image="https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/4d201742-e1e0-47d3-b7f0-262dcec8e911/encryption_at_rest.png" data-image-dimensions="845x453" data-image-focal-point="0.5,0.5" alt="" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/4d201742-e1e0-47d3-b7f0-262dcec8e911/encryption_at_rest.png?format=1000w" width="845" height="453" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" onload="this.classList.add(&quot;loaded&quot;)" srcset="https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/4d201742-e1e0-47d3-b7f0-262dcec8e911/encryption_at_rest.png?format=100w 100w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/4d201742-e1e0-47d3-b7f0-262dcec8e911/encryption_at_rest.png?format=300w 300w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/4d201742-e1e0-47d3-b7f0-262dcec8e911/encryption_at_rest.png?format=500w 500w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/4d201742-e1e0-47d3-b7f0-262dcec8e911/encryption_at_rest.png?format=750w 750w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/4d201742-e1e0-47d3-b7f0-262dcec8e911/encryption_at_rest.png?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/4d201742-e1e0-47d3-b7f0-262dcec8e911/encryption_at_rest.png?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/4d201742-e1e0-47d3-b7f0-262dcec8e911/encryption_at_rest.png?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs">

            
          
        
          
        

        
      
        </figure>
      

    
  


  





  <p class="">The encryption service offers 3 methods for encrypting your data on the underlying infrastructure. The first is the default which uses Microsoft-managed encryption keys and follows the flow above. Anyone with API access to the Azure storage account will be able to read the data, and, all data stored within the Azure storage account is encrypted using the same key. Microsoft manages the key, which effectively means that there is a key rotation process that happens in the background.</p><p class="">The second method is to use customer-managed keys. Here is the diagram from the <a href="https://docs.microsoft.com/en-us/azure/storage/common/customer-managed-keys-overview">docs</a> page.</p>





















  
  














































  

    
  
    

      

      
        <figure class="
              sqs-block-image-figure
              intrinsic
            "
        >
          
        
        

        
          
            
          
            
                
                
                
                
                
                
                
                <img data-stretch="false" data-image="https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/cfaf4f45-4ae0-47e2-a7db-19628cdfd49d/encryption-customer-managed-keys-diagram.png" data-image-dimensions="624x202" data-image-focal-point="0.5,0.5" alt="" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/cfaf4f45-4ae0-47e2-a7db-19628cdfd49d/encryption-customer-managed-keys-diagram.png?format=1000w" width="624" height="202" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" onload="this.classList.add(&quot;loaded&quot;)" srcset="https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/cfaf4f45-4ae0-47e2-a7db-19628cdfd49d/encryption-customer-managed-keys-diagram.png?format=100w 100w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/cfaf4f45-4ae0-47e2-a7db-19628cdfd49d/encryption-customer-managed-keys-diagram.png?format=300w 300w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/cfaf4f45-4ae0-47e2-a7db-19628cdfd49d/encryption-customer-managed-keys-diagram.png?format=500w 500w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/cfaf4f45-4ae0-47e2-a7db-19628cdfd49d/encryption-customer-managed-keys-diagram.png?format=750w 750w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/cfaf4f45-4ae0-47e2-a7db-19628cdfd49d/encryption-customer-managed-keys-diagram.png?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/cfaf4f45-4ae0-47e2-a7db-19628cdfd49d/encryption-customer-managed-keys-diagram.png?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/cfaf4f45-4ae0-47e2-a7db-19628cdfd49d/encryption-customer-managed-keys-diagram.png?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs">

            
          
        
          
        

        
      
        </figure>
      

    
  


  





  <p class="">In this method, an encryption key is still stored as part of the encryption service, just like in the first scenario. The difference here is that the key stored in the encryption service is wrapped by a customer managed key. This customer-managed key acts as a key-encrypting key and is stored in Azure Key Vault.</p><p class="">The main benefit here would be around deletion of the data itself. Rendering the data irretrievable in this scenario would be as simple as deleting the customer managed key in the Azure Key Vault. This benefit comes at the cost of some important trade-offs. The first is that Azure Key Vault is priced in a per-operation perspective. Because the encryption service within the Azure storage account needs to decrypt the encryption key in order to use it, every operation will incur a call to Azure Key vault. Further, each call could result in increased latency as you’ve now added another service into the overall data flow. The last trade-off is that your customer managed key becomes a key that you will also have to rotate/manage/control.</p><p class="">The last method is called the customer-provided key. In this method, the flow above is the same, however, the encryption service no longer stores the encryption key (in any form) for the back-end data. Instead, the client sends up the key to use during both the read and write operations. This method provides the most control over which key is used to encrypt the data in the storage account at the expense of passing all the key management and distribution responsibilities to the client.</p><h3><strong>Encryption Scopes (Preview)</strong></h3><p class="">A feature that is currently in preview for Azure storage is <a href="https://docs.microsoft.com/en-us/azure/storage/common/storage-service-encryption#encryption-scopes-for-blob-storage-preview">encryption scopes</a>. Based on my understanding of the feature, encryption scopes is designed to be an alternative to customer-provided keys. Effectively, you can now define multiple encryption keys that can be used in a given storage account. You can then define the default key to be used (at the container level, for example) and you can also specify which key to be used when individual blobs are created (as an override, perhaps).</p><p class="">The benefit here, over customer-provided keys, is that you don’t have to manage the storage and distribution of the key to all the various clients that would need access to the data. The disadvantage is that I do not believe there is any access controls on an encryption scope itself. Therefore, any client interacting with storage could use any of the encryption scopes currently defined.</p><h4><strong>A use case</strong></h4><p class="">At Keep Secure, we work with many start-ups that deal with customer data in a multi-tenant fashion. A potential use-case for encryption scopes is to allow for multi-tenant storage of data while giving customers fine grain control over the data stored.</p><p class="">For example, a SaaS provider could provision a key vault for each customer for whom they want to store data for. Using key vault authentication, they could provide access to customers to create a customer-managed key within that key vault. Then, they would create an encryption-scope tied to that customer which uses the created/shared key-vault. Now, all data tied to a given customer could be encrypted with a customer-specific encryption scope, tied to a key to which the end customer has full control over.</p><p class="">One of the key benefits I see for this approach is in off-boarding scenarios. With a simple action of deleting the customer-managed key in the key vault, all data stored against that key would be rendered useless. An effective way to provide various assurances to customers that data deletion was done properly.</p><h3><strong>Conclusion</strong></h3><p class="">There are a couple of neat new features as it relates to Azure storage that I hope to explore. Encryption scopes is one of them. I think this could provide some very interesting features particularly as it relates to data security and privacy regulations. I hope you enjoyed.</p>]]></content:encoded><media:content type="image/png" url="https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/1713469443411-CR39W4E1PNWP5YRBT79M/azure_storage_encryption.png?format=1500w" medium="image" isDefault="true" width="1493" height="995"><media:title type="plain">Azure Storage Encryption At Rest</media:title></media:content></item><item><title>Using Azure Custom Roles to Secure your Azure Data Factory Resources</title><dc:creator>Todd Willsie</dc:creator><pubDate>Sat, 19 Sep 2020 17:01:00 +0000</pubDate><link>https://www.jot.digital/blog/using-azure-custom-roles-to-secure-your-azure-data-factory-resources</link><guid isPermaLink="false">647fb19e93051a0534471f45:66215170b81b27326eed8167:6619941c85a73a5b866b53eb</guid><description><![CDATA[Azure data factory (ADF) is billed as an Extract/Transform/Load (ETL) tool 
that has a code-free interface for designing, deploying, and monitoring 
data pipelines. There is a lot you can do with the tool, and one of the 
interesting design features is that it is all built on top of Azure 
Resource Manager (ARM). One issue that arises due to this approach is that 
both development and operations of the ADF resource is done in the same 
place.]]></description><content:encoded><![CDATA[<p class="">Azure data factory (ADF) is billed as an Extract/Transform/Load (ETL) tool that has a code-free interface for designing, deploying, and monitoring data pipelines. There is a lot you can do with the tool, and one of the interesting design features is that it is all built on top of Azure Resource Manager (ARM). One issue that arises due to this approach is that both development and operations of the ADF resource is done in the same place. This leads to some obvious questions such as:</p><p class="">1. Who can make what types of changes to your ADF resources?</p><p class="">2. In what environments do you allow certain change types?</p><p class="">3. Who is responsible for making changes "operational"?<br></p><p class="">The goal of this post is to discuss how to use <a href="https://docs.microsoft.com/en-us/azure/role-based-access-control/custom-roles" target="_blank">Azure Custom Roles</a> to help secure your ADF resources in both development and operations. In order to understand how to use this, we need to walk through a couple of concepts.</p><p data-rte-preserve-empty="true" class=""></p><h3><strong>ADF Components</strong></h3><p class="">As per the documentation <a href="https://docs.microsoft.com/en-us/azure/data-factory/introduction#top-level-concepts" target="_blank">here</a> there are several components that actually make up an ADF. These include developer components, such as pipelines and activities, data components, such as datasets, and then operational components, such as linked services and triggers.</p><p class="">When you develop or interact with ADF via the portal, you get to use a drag/drop interface to effectively configure an ARM template representation of that particular component. Each one is actually backed by a schema, and you can view these <a href="https://docs.microsoft.com/en-us/azure/templates/microsoft.datafactory/allversions" target="_blank">here</a>.</p><p class="">Because the underlying service uses ARM as it's configuration engine, there is also an associated ARM Resource Provider which handles all the calls to create/update an ADF instance. You can view a full list of all the actions you can take <a href="https://docs.microsoft.com/en-us/azure/role-based-access-control/resource-provider-operations#microsoftdatafactory" target="_blank">here</a>.</p><p data-rte-preserve-empty="true" class=""></p><h3><strong>What are Azure Custom Roles?</strong></h3><p class="">If you've ever granted access to anyone to Azure resources, you've likely had to give them permissions to a target resource, resource group, or subscription. Likely, you've played around with several of the <a href="https://docs.microsoft.com/en-us/azure/role-based-access-control/built-in-roles" target="_blank">built-in roles</a>, including the most common ones of Contributor, Owner, or Reader. Over the last while, Azure had greatly expanded the amount of built-in roles you can use, getting more granular with how permissions are applied.</p><p class="">All role definitions are effectively a set of Actions (or not-actions in the case of deny) that a role can take on the target. In the case of ADF, Azure provides one built-in role in additional to the default, called <a href="https://docs.microsoft.com/en-us/azure/role-based-access-control/built-in-roles#data-factory-contributor" target="_blank">Data Factory Contributor</a>.</p><p class="">Depending on your development and operations process, this single built-in role might not provide enough granularity. For example, in production, your support personnel would be able to change linked service configuration using the Data Factory Contributor role. You might not want this as it could be a potential security issue.</p><p class="">Azure custom roles effectively allow you to build your own permission sets that you can use to grant to users or groups in Azure. This allows you to get more fine grained with your permission model.</p><p data-rte-preserve-empty="true" class=""></p><h3><strong>ADF Personas</strong></h3><p class="">With all the pre-amble out of the way, lets talk about a couple of different personas you might have. It is important to keep in mind that you will likely also have distinctions between environments as to who has access to what. Here is a stab:</p><p class=""><strong>Operator</strong><br>An ADF operator is someone who manages the health of an ADF instance. They could, for example, monitor job runs, queue lengths, etc. They would never make changes to the ADF itself in terms of data pipeline code.</p><p class=""><strong>Developer<br></strong>An ADF developer is someone who would have access to make changes to developer relates components such as pipelines, activities, and data sets. They wouldn't create or modify linked services, and they wouldn't schedule triggers or other runs.</p><p class=""><strong>Admin<br></strong>This is your infrastructure administrator who is responsible for the ADF itself. Typically, they would have full access to the ADF. Most importantly, they would be the ones creating/modifying security settings, repository settings, and linked services.</p><p class=""><strong>Data<br></strong>This role might make sense for a subset of developers who are responsible for creating/maintaining datasets and the various configurations in that dataset. You could create a data role separate from your developer role.</p><p data-rte-preserve-empty="true" class=""></p><h3><strong>Creating Custom Roles</strong></h3><p class="">A full discussion around creating custom roles is out of scope for this post, but here is an example of an implemented operator role.</p>





















  
  



<pre><code>{
    "Name":"Data Factory Operator",
    "Id": "",
    "IsCustom": true,
    "Description":"A custom role that grants users access to operate ADF",
    "Actions":[
        "Microsoft.Authorization/*/read",
        "Microsoft.Resources/subscriptions/resourceGroups/read",
        "Microsoft.ResourceHealth/availabilityStatuses/read",
        "Microsoft.DataFactory/datafactories/*/read",
        "Microsoft.DataFactory/factories/*/read",
        "Microsoft.Resources/deployments/*/read",
        "Microsoft.DataFactory/datafactories/datapipelines/pause/action",
        "Microsoft.DataFactory/datafactories/datapipelines/resume/action",
        "Microsoft.DataFactory/datafactories/gateways/connectioninfo/action",
        "Microsoft.DataFactory/factories/cancelpipelinerun/action",
        "Microsoft.DataFactory/factories/pipelines/createrun/action",
        "Microsoft.DataFactory/factories/pipelineruns/cancel/action",
        "Microsoft.DataFactory/factories/triggers/start/action",
        "Microsoft.DataFactory/factories/triggers/stop/action",
        "Microsoft.DataFactory/factories/pipelines/sandbox/action",
        "Microsoft.DataFactory/factories/pipelines/sandbox/create/action",
        "Microsoft.DataFactory/factories/pipelines/sandbox/run/action",
        "Microsoft.DataFactory/factories/getDataPlaneAccess/action"
    ],
    "NotActions": [
    ],
    "AssignableScopes": [
      "/an/assignable/scope"
    ]
}
</code></pre>



  <p class="">The goal of the above role is to give the operator enough permissions to monitor the ADF resource and restart jobs as required.</p><p data-rte-preserve-empty="true" class=""></p><h3><strong>Conclusion</strong></h3><p class="">ADF is a pretty powerful tool, but combines the development and operation planes together. This separation is still required for both security reasons and development process reasons. While you can use policy to hopefully enforce separation of concerns, Azure Custom Roles provides an effective way of enforcing the same via Azure RBAC.</p>]]></content:encoded><media:content type="image/png" url="https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/1712952916792-KJXEWQN4FIBRMNLJXHPM/adf_custom_roles.png?format=1500w" medium="image" isDefault="true" width="1207" height="805"><media:title type="plain">Using Azure Custom Roles to Secure your Azure Data Factory Resources</media:title></media:content></item><item><title>Fixing ADF Databricks Linked Service</title><dc:creator>Todd Willsie</dc:creator><pubDate>Fri, 31 Jul 2020 19:22:00 +0000</pubDate><link>https://www.jot.digital/blog/fixing-adf-databricks-linked-service</link><guid isPermaLink="false">647fb19e93051a0534471f45:66215170b81b27326eed8167:66217107379bfe0653e621ec</guid><description><![CDATA[Azure Data Factory (ADF) is effectively a programming model built upon 
Azure Resource Manager (ARM) templates. One way to automate deployments of 
ADF is to make use of ARM template exports which are automatically exported 
when you click the publish button.]]></description><content:encoded><![CDATA[<p class="">Azure Data Factory (ADF) is effectively a programming model built upon Azure Resource Manager (ARM) templates. One way to automate deployments of ADF is to make use of ARM template exports which are automatically exported when you click the <a href="https://docs.microsoft.com/en-us/azure/data-factory/continuous-integration-deployment#overview">publish button</a>.</p><p class="">You can use linked services to interface ADF with other services, such as Azure Databricks. Here is what the configuration for an Azure Databricks linked service looks like:</p>





















  
  














































  

    
  
    

      

      
        <figure class="
              sqs-block-image-figure
              intrinsic
            "
        >
          
        
        

        
          
            
          
            
                
                
                
                
                
                
                
                <img data-stretch="false" data-image="https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/6f59f1ae-1ac7-4680-9245-02648658e355/azure_databricks_ls.png" data-image-dimensions="624x980" data-image-focal-point="0.5,0.5" alt="" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/6f59f1ae-1ac7-4680-9245-02648658e355/azure_databricks_ls.png?format=1000w" width="624" height="980" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" onload="this.classList.add(&quot;loaded&quot;)" srcset="https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/6f59f1ae-1ac7-4680-9245-02648658e355/azure_databricks_ls.png?format=100w 100w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/6f59f1ae-1ac7-4680-9245-02648658e355/azure_databricks_ls.png?format=300w 300w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/6f59f1ae-1ac7-4680-9245-02648658e355/azure_databricks_ls.png?format=500w 500w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/6f59f1ae-1ac7-4680-9245-02648658e355/azure_databricks_ls.png?format=750w 750w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/6f59f1ae-1ac7-4680-9245-02648658e355/azure_databricks_ls.png?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/6f59f1ae-1ac7-4680-9245-02648658e355/azure_databricks_ls.png?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/6f59f1ae-1ac7-4680-9245-02648658e355/azure_databricks_ls.png?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs">

            
          
        
          
        

        
      
        </figure>
      

    
  


  





  <p class="">In the image above, there are several items that you may want to change between environments that this linked service is deployed to. Moreover, Azure Databricks recently announced a move from generic URLs to <a href="https://docs.microsoft.com/en-ca/azure/databricks/workspace/migrate-workspace-urls">per-workspace URLs</a>.</p><p class="">Unfortunately, by default, the “domain” (Databricks Workspace URL in the image above) attribute of an Azure Databricks linked service is not exposed in the ARM template parameters file. Therefore, it cannot be overridden easily at deployment time.</p><p class="">Luckily, the ADF team opted to have a configuration file determine which components of the ARM template are exposed to a parameters file, and which ones are not. You can read more about it <a href="https://docs.microsoft.com/en-us/azure/data-factory/continuous-integration-deployment#use-custom-parameters-with-the-resource-manager-template">here</a>.</p><p class="">Taking a look at the default configuration, one notices that the “domain” keyword is missing from the parameters list.</p>





















  
  



<pre class="source-code">    &quot;Microsoft.DataFactory/factories/linkedServices&quot;: {
        &quot;*&quot;: {
            &quot;properties&quot;: {
                &quot;typeProperties&quot;: {
                    &quot;accountName&quot;: &quot;=&quot;,
                    &quot;username&quot;: &quot;=&quot;,
                    &quot;userName&quot;: &quot;=&quot;,
                    &quot;accessKeyId&quot;: &quot;=&quot;,
                    &quot;servicePrincipalId&quot;: &quot;=&quot;,
                    &quot;userId&quot;: &quot;=&quot;,
                    &quot;clientId&quot;: &quot;=&quot;,
                    &quot;clusterUserName&quot;: &quot;=&quot;,
                    &quot;clusterSshUserName&quot;: &quot;=&quot;,
                    &quot;hostSubscriptionId&quot;: &quot;=&quot;,
                    &quot;clusterResourceGroup&quot;: &quot;=&quot;,
                    &quot;subscriptionId&quot;: &quot;=&quot;,
                    &quot;resourceGroupName&quot;: &quot;=&quot;,
                    &quot;tenant&quot;: &quot;=&quot;,
                    &quot;dataLakeStoreUri&quot;: &quot;=&quot;,
                    &quot;baseUrl&quot;: &quot;=&quot;,
                    &quot;database&quot;: &quot;=&quot;,
                    &quot;serviceEndpoint&quot;: &quot;=&quot;,
                    &quot;batchUri&quot;: &quot;=&quot;,
                    &quot;databaseName&quot;: &quot;=&quot;,
                    &quot;systemNumber&quot;: &quot;=&quot;,
                    &quot;server&quot;: &quot;=&quot;,
                    &quot;url&quot;: &quot;=&quot;,
                    &quot;aadResourceId&quot;: &quot;=&quot;,
                    &quot;connectionString&quot;: &quot;|:-connectionString:secureString&quot;,
                    &quot;existingClusterId&quot;: &quot;=&quot;,
                    &quot;host&quot;: &quot;=&quot;,
                    &quot;secretName&quot;: &quot;=&quot;
                }
            }
        },</pre>


  <p class="">You can simply add “domain”:"-", under type properties above to have that property now exposed. For reference, the - means that no default should be given to the parameter, forcing you to override it at deployment time. This is likely what you want given you hopefully are not reusing workspaces between environments.</p><p class="">What is really nice is that you can make use of the new Azure Data Factory <a href="https://docs.microsoft.com/en-us/azure/data-factory/author-management-hub#parameterization-template">Management Hub</a> to expose an editor to help you make these changes.</p><p class="">Hope that helps!</p>]]></content:encoded><media:content type="image/png" url="https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/1713468161124-WK65Q3JMRUON903P5VTQ/fixing_adf_databricks_linked_service.png?format=1500w" medium="image" isDefault="true" width="1085" height="816"><media:title type="plain">Fixing ADF Databricks Linked Service</media:title></media:content></item><item><title>Exploring Azure Databricks Permissions</title><dc:creator>Todd Willsie</dc:creator><pubDate>Sat, 15 Feb 2020 21:54:00 +0000</pubDate><link>https://www.jot.digital/blog/exploring-azure-databricks-permissions</link><guid isPermaLink="false">647fb19e93051a0534471f45:66215170b81b27326eed8167:66218853d033291dab682092</guid><description><![CDATA[We are continuing on with our discussion about devops and security concerns 
with Azure Databricks. In this post, we will talk about setting up granular 
permissions inside of Azure Databricks.]]></description><content:encoded><![CDATA[<p class="">We are continuing on with our discussion about devops and security concerns with Azure Databricks. In this post, we will talk about setting up granular permissions inside of Azure Databricks.</p><p class="">By default, particularly with workspaces in the standard tier, all users have access to all resources within the workspace. By resources, I mean specific Databricks “objects” such as directories, notebooks, clusters, pools, jobs and tables. Luckily, Azure Databricks offers a <a href="https://databricks.com/product/azure-pricing">premium plan</a>, which allows administrators to configure custom role-based access controls based on the permissions API.</p><p class="">When you are creating production Databricks workspaces, you are likely going to have two main use-cases. The first is job specific. This workspace is used to run pre-created reports and functions that have followed some type of development process and have been promoted into production. The second type is going to be more for exploratory type processing. End users will want to experiment and play with the data, creating notebooks in an interactive fashion and examining the results.</p><p class="">From a job specific workspace perspective, you likely want to have creation of new notebooks, jobs, clusters, etc locked down to only approved CI/CD processes. Because these jobs will likely be using service principals, ensuring that users cannot just create notebooks and run them would be of extreme importance. Of course, you will still have support personnel who will need to monitor job execution and results. Azure Databricks role-based access control can help with this use case.</p><p class="">For interactive clusters, you will likely want to ensure that users have “safe” places to create their notebooks, run jobs, and examine results. Because the results of the notebooks are stored with the notebook themselves, you’ll want to create appropriate role-based access controls to ensure that only users with the same security clearance can see their outputs. You may also want to create “home” directories only accessible by the individual users. Again, role-based access control is a good fit here.</p><h2><strong>Permissions Architecture</strong></h2><p class="">From an architecture perspective, the permissions in Azure Databricks is quite simplistic. Each object within a Databricks workspace (for example a notebook) has a set of “permissions” that can be associated with it.</p><p class="">For example, notebooks can have the following permissions:</p><ul data-rte-list="default"><li><p class="">CAN_READ</p><ul data-rte-list="default"><li><p class="">Users can view and comment on a notebook</p></li></ul></li><li><p class="">CAN_RUN</p><ul data-rte-list="default"><li><p class="">Users can view, comment and also attach/detach the notebook from a cluster. They can also run commands within that notebook</p></li></ul></li><li><p class="">CAN_EDIT</p><ul data-rte-list="default"><li><p class="">All the above plus the ability to edit the notebook</p></li></ul></li><li><p class="">CAN_MANAGE</p><ul data-rte-list="default"><li><p class="">All the above and can also change permissions on the notebook</p></li></ul></li></ul><p class="">These permissions can be assigned to the respective objects along with a user or a group. This typically manifests itself as adding an access control list to that particular object. Generically, it looks something like this:</p>





















  
  



<pre class="source-code">{
	&quot;access_control_list&quot;​: [
	{
		&quot;user_name&quot;​:​&quot;&lt;UserName&gt;&quot;​ || ​&quot;group_name&quot;​:​&quot;&lt;GroupName&gt;&quot; ,
		​&quot;permission_level&quot;​: ​&quot;&lt;PermissionLevel&gt;
	}
	]
} </pre>


  <h2><strong>Conclusion</strong></h2><p class="">At time of writing, permissions can be used in premium tier workspaces with <a href="https://docs.microsoft.com/en-us/azure/databricks/administration-guide/access-control/">workspace access control</a> enabled. It is editable via the portal experience, and, if you ask nicely, you may get access to a preview for setting the permissions via script.</p>]]></content:encoded><media:content type="image/png" url="https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/1713473678544-XC6NVQVO5RY7CI1TTD0Q/azure_databricks_exploring_permissions.png?format=1500w" medium="image" isDefault="true" width="834" height="407"><media:title type="plain">Exploring Azure Databricks Permissions</media:title></media:content></item><item><title>AWS vs. Azure - Object Storage Blob Access Part 1</title><dc:creator>Todd Willsie</dc:creator><pubDate>Wed, 16 Jan 2019 21:22:00 +0000</pubDate><link>https://www.jot.digital/blog/aws-vs-azure-object-storage-blob-access-part-1</link><guid isPermaLink="false">647fb19e93051a0534471f45:66215170b81b27326eed8167:6621890637b2c00637f34f6c</guid><description><![CDATA[For both AWS and Azure, a REST based API has been created to facilitate 
blob storage operations. The REST API has two logical components, a data 
plane (interacting with blobs themselves) and a management plane 
(interacting with the service itself). The goal of this post is to talk 
about access options at the data plane level.]]></description><content:encoded><![CDATA[<p class="">For both AWS and Azure, a REST based API has been created to facilitate blob storage operations. The REST API has two logical components, a data plane (interacting with blobs themselves) and a management plane (interacting with the service itself). The goal of this post is to talk about access options at the data plane level.</p>





















  
  



<table width="100%">
<thead>
<tr>
<th>AWS</th>
<th>Azure</th>
</tr>
</thead>
<tbody>
<tr>
<td>Authentication Type</td>
<td><ul><li>Root Account</li><li>IAM Users</li></ul>&nbsp;</td>
</tr>
<tr>
<td>Access Policies</td>
<td><ul><li>IAM Roles</li><li>Bucket Policies</li><li>Object Policies</li><li>Storage ACL</li></ul>&nbsp;</td>
</tr>
<tr>
<td>Anonymous Access</td>
<td>Yes</td>
</tr>
</tbody>
</table>


  <p class="">The term “bucket” as it relates to object storage is a term almost as old as the term “cloud computing”. In AWS, a bucket is effectively described as a container for objects. They are the highest level of organization for objects.</p><p class="">Because buckets are the only organization option available, general settings such as versioning, ACLs, default encryption, replication, logging, and lifecycle are set at this level.</p><p class="">In Azure, you create storage accounts that have child resources called containers. All blobs must live in a container. At a high level, account level settings are similar to the bucket level settings for AWS with the exception of access policies. In general, access policies are set/maintained at the container level.</p><h3><strong>Authentication Types</strong></h3><p class="">Both AWS and Azure have their services setup such that permissions on the management plane can bleed into permissions on the data plane.</p><p class="">In the case of AWS, identities for object storage access are either the root account or IAM users. Depending on which is in use, different authentication flows can be utilized. IAM users have the added steps of verifying authentication information and policies with IAM before making the request to S3. Here is an example of a authentication flow that shows all the options.</p>





















  
  














































  

    
  
    

      

      
        <figure class="
              sqs-block-image-figure
              intrinsic
            "
        >
          
        
        

        
          
            
          
            
                
                
                
                
                
                
                
                <img data-stretch="false" data-image="https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/fde81be1-2810-496d-93c9-c37681720cb8/aws-azure-blob-part-1-diagram.png" data-image-dimensions="874x521" data-image-focal-point="0.5,0.5" alt="" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/fde81be1-2810-496d-93c9-c37681720cb8/aws-azure-blob-part-1-diagram.png?format=1000w" width="874" height="521" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" onload="this.classList.add(&quot;loaded&quot;)" srcset="https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/fde81be1-2810-496d-93c9-c37681720cb8/aws-azure-blob-part-1-diagram.png?format=100w 100w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/fde81be1-2810-496d-93c9-c37681720cb8/aws-azure-blob-part-1-diagram.png?format=300w 300w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/fde81be1-2810-496d-93c9-c37681720cb8/aws-azure-blob-part-1-diagram.png?format=500w 500w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/fde81be1-2810-496d-93c9-c37681720cb8/aws-azure-blob-part-1-diagram.png?format=750w 750w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/fde81be1-2810-496d-93c9-c37681720cb8/aws-azure-blob-part-1-diagram.png?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/fde81be1-2810-496d-93c9-c37681720cb8/aws-azure-blob-part-1-diagram.png?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/fde81be1-2810-496d-93c9-c37681720cb8/aws-azure-blob-part-1-diagram.png?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs">

            
          
        
          
        

        
      
        </figure>
      

    
  


  





  <p class="">Important to note is that bucket owners are owners on all objects inside that bucket, regardless of any policies set within that bucket. See <a href="https://docs.aws.amazon.com/AmazonS3/latest/dev/access-control-overview.html">https://docs.aws.amazon.com/AmazonS3/latest/dev/access-control-overview.html</a> for more information. Obviously, the guidance from AWS is to not use root credentials for access to storage accounts, rather create an IAM user and apply appropriate policies.</p><p class="">Azure storage functions in a very similar fashion to AWS S3. Firstly, anyone with appropriate permissions to the management plane of a storage account can also have full access to all items in the storage account. This is controlled via access to one of the two storage access keys (shared keys) that are provisioned with every account. RBAC permissions here are fairly important (which we will talk about later).</p><p class="">SAS tokens were originally introduced to solve issues with using shared keys for temporary access to the object storage. SAS tokens provide a way of generating temporary credentials that can be used to a subset of permissions for a period of time.</p><p class="">SAS tokens are not associated with users, which has inherent benefits and tradeoffs. The benefit is that I don’t need to have a user object associated with each token. The tradeoff is that managing and revoking SAS tokens becomes a bit of a chore. In a way, SAS Policies can be seen as an entity equivalent to IAM users. AWS STS can be seen (used) in a similar fashion to SAS tokens.</p><p class="">In short, it seems like both solutions (while architected quite differently) facilitate many of the common use cases one may require.</p><p class="">1 | Administrative access to buckets and objects</p><p class="">In AWS and Azure this is inherent in the design. Having appropriate access at the management plane level allows for access at the data plane level. In AWS, this is done via the policy system whereas in Azure this is done by controlling access to the shared access keys.</p><p class="">2 | Granular user access</p><p class="">In AWS, this type of access is easily provisioned as part of IAM users/roles configuration. In Azure, SAS tokens are used to provide granular access, but are not tied to users specifically. Concepts such as SAS Policies can be used to facilitate this. AAD based permissions (currently in preview) provides a better story around this use case.</p><p class="">3 | Temporary credentials</p><p class="">The valet key pattern is a core pattern from an application architecture standpoint. In Azure, this is easily accomplished via SAS tokens and policies. Each request can generate an appropriate SAS token which expires automatically.</p><p class="">In AWS, this is likely a combination of IAM users and AWS STS for issuing temporary credentials.</p>]]></content:encoded><media:content type="image/jpeg" url="https://images.squarespace-cdn.com/content/v1/647fb19e93051a0534471f45/1713558173083-X46CA20MYTNKIED2M20X/aws-vs-azure.jpg?format=1500w" medium="image" isDefault="true" width="1200" height="800"><media:title type="plain">AWS vs. Azure - Object Storage Blob Access Part 1</media:title></media:content></item></channel></rss>