<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Danilo Desole</title>
    <description>The latest articles on DEV Community by Danilo Desole (@panilo).</description>
    <link>https://dev.to/panilo</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F621881%2F26f7b1c5-b72d-4fab-a109-156ace952549.JPG</url>
      <title>DEV Community: Danilo Desole</title>
      <link>https://dev.to/panilo</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/panilo"/>
    <language>en</language>
    <item>
      <title>Wordpress 403 errors</title>
      <dc:creator>Danilo Desole</dc:creator>
      <pubDate>Fri, 16 May 2025 07:53:05 +0000</pubDate>
      <link>https://dev.to/panilo/wordpress-403-errors-2ebf</link>
      <guid>https://dev.to/panilo/wordpress-403-errors-2ebf</guid>
      <description>&lt;p&gt;Ever wonder why your WordPress website suddenly starts showing you a 403 error when you try to log in to the &lt;code&gt;wp-admin&lt;/code&gt; side? It happened to me recently, and I had to do a LOT of investigation before figuring out what was wrong. All the blog posts I found suggested the following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Check File and Folder Permissions – Your WordPress directories should have 755 permissions, and files should be 644. You can adjust these via FTP or your hosting panel.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Inspect the &lt;code&gt;.htaccess&lt;/code&gt; File – Sometimes, hosting providers modify this file to prevent brute-force attacks, which can inadvertently block access. Try renaming it temporarily to see if that resolves the issue.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Whitelist Your IP Address – If your host has strict security settings, you may need to whitelist your IP to regain access.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Disable Security Plugins – Some security plugins may block admin access. Try renaming the plugin folder via FTP to disable it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Check for Malware – If your site has been compromised, malware could be restricting access. Running a security scan might help.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Contact Your Hosting Provider – If none of the above work, your host might have firewall rules or restrictions causing the issue. They should be able to assist.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;None of the above worked for me, except that I did not contact my hosting provider, given that I was copying a website from another host where it was working perfectly. So what happened? The reason my WordPress website was returning a 403 error when I tried to log in to the &lt;code&gt;wp-admin&lt;/code&gt; area lay in a database dump.&lt;/p&gt;

&lt;p&gt;To give some background on my process, I was &lt;em&gt;moving&lt;/em&gt; a website from one hosting provider to another. I proceeded with the usual file backup and database dump. Then, I moved the files to the new hosting and imported the database dump into the new database server. I modified the &lt;code&gt;wp-config&lt;/code&gt; file, and then the first error occurred—WordPress was telling me that tables did not exist.&lt;/p&gt;

&lt;p&gt;Checking the &lt;code&gt;wp-config&lt;/code&gt; file, everything seemed correct. However, upon closer inspection, I noticed that the database prefix in my &lt;code&gt;wp-config&lt;/code&gt; file was uppercase, while the imported table prefix was lowercase. Easy fix, I thought! I simply updated the &lt;code&gt;wp-config&lt;/code&gt; file with the lowercase prefix.&lt;/p&gt;

&lt;p&gt;Then, the website was able to run, I had access to the end-user front-end. However, when I accessed the &lt;code&gt;wp-admin&lt;/code&gt; area, a 403 error occurred. After trying all the above solutions, none helped. I took another look at the database, and by chance, I noticed that in the &lt;code&gt;wp-usermeta&lt;/code&gt; table, some rows contained the table prefix in the &lt;code&gt;meta_key&lt;/code&gt; field!&lt;/p&gt;

&lt;p&gt;Updating the database dump to use the uppercase prefix, re-importing it, and finally updating the &lt;code&gt;wp-config&lt;/code&gt; file helped me fix the issue.&lt;/p&gt;

&lt;p&gt;A few things went wrong:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;PhpMyAdmin exported the database with a lowercase table prefix (why?).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;I had an initial issue where WordPress was not able to find some tables in the database, and I ignored it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;I updated the &lt;code&gt;wp-config&lt;/code&gt; file table prefix without actually modifying the database.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let me know what you think about this article—it's my first one in this community, and I really appreciate your feedback to improve.&lt;/p&gt;

&lt;p&gt;Cheers!&lt;/p&gt;

</description>
      <category>wordpress</category>
    </item>
    <item>
      <title>Boto3 and Python unittest.mock</title>
      <dc:creator>Danilo Desole</dc:creator>
      <pubDate>Wed, 24 Apr 2024 10:30:03 +0000</pubDate>
      <link>https://dev.to/panilo/boto3-and-python-unittestmock-5hji</link>
      <guid>https://dev.to/panilo/boto3-and-python-unittestmock-5hji</guid>
      <description>&lt;p&gt;I start this post by saying I'm not a professional software developer, I work mainly in IT Operations, although I write especially for IAC and small lambdas functions. &lt;/p&gt;

&lt;p&gt;When developing a Lambda function most of the time I need to interact with AWS Services via the famous &lt;code&gt;boto3&lt;/code&gt; library; &lt;code&gt;boto3&lt;/code&gt; is a powerful library developed and maintained by AWS which provides a communication framework to interact with native AWS Cloud Services. &lt;/p&gt;

&lt;p&gt;Every time I struggle to mock the library. I even considered using the &lt;code&gt;moto&lt;/code&gt; stubber but, I'm not happy with what is provided. I aim to &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a mock which can stub the original call&lt;/li&gt;
&lt;li&gt;I want the mock to expose all the methods to check which parameters have been used during the calls, how many times it has been called, etc... all the stuff included within the &lt;a href="https://docs.python.org/3/library/unittest.mock.html" rel="noopener noreferrer"&gt;&lt;code&gt;Mock&lt;/code&gt;&lt;/a&gt; class &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In this example, we will mock &lt;code&gt;boto3&lt;/code&gt; while it creates a client for &lt;code&gt;RDS&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Consider the following code&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyRDSManager&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_rds_client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rds&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;delete_db_cluster_snapshot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_rds_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;delete_db_cluster_snapshot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DBClusterSnapshotIdentifier&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ABC&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_snapshots&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="n"&gt;snapshots&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

        &lt;span class="n"&gt;paginator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_rds_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_paginator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;describe_db_cluster_snapshots&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;pages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;paginator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;paginate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DBClusterIdentifier&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;MyCluster&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;pages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;page_snapshots&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DBClusterSnapshots&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;snapshot&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;page_snapshots&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;snapshots&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;snapshot&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;snapshots&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To test the above class I developed the following tests&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pytest&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;unittest.mock&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Mock&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;patch&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;MyRDSManager&lt;/span&gt;


&lt;span class="nd"&gt;@pytest.fixture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;function&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;prepare_mock&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;patch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;main.boto3.client&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;mock_boto_client&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# the first thing to do is to set the return_value attribute as itself
&lt;/span&gt;        &lt;span class="c1"&gt;# this will return the mock itself when the code runs `boto3.client("rds")`
&lt;/span&gt;        &lt;span class="n"&gt;mock_boto_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;return_value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mock_boto_client&lt;/span&gt;

        &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;mock_paginate_describe_db_cluster_snapshots&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;snapshot_type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SnapshotType&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# get all the parameter passed to the call
&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DBClusterSnapshots&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DBClusterSnapshotIdentifier&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ABC&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SnapshotCreationTime&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2024-01-01&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SnapshotType&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;manual&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;]}]&lt;/span&gt;

        &lt;span class="n"&gt;mock_boto_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_paginator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Mock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;mock_paginator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Mock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;return_value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;mock_paginator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;paginate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Mock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;return_value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;mock_paginator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;paginate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;side_effect&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mock_paginate_describe_db_cluster_snapshots&lt;/span&gt;

        &lt;span class="n"&gt;mock_boto_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_paginator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;return_value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mock_paginator&lt;/span&gt;

        &lt;span class="n"&gt;mock_boto_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;delete_db_cluster_snapshot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Mock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;return_value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;my_rds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;MyRDSManager&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;my_rds&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mock_boto_client&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_one&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prepare_mock&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;mock_my_rds&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mock_boto_client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;prepare_mock&lt;/span&gt;

    &lt;span class="n"&gt;mock_my_rds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;delete_db_cluster_snapshot&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;mock_boto_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;delete_db_cluster_snapshot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assert_called_once_with&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DBClusterSnapshotIdentifier&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ABC&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mock_my_rds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_snapshots&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DBClusterSnapshotIdentifier&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ABC&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SnapshotCreationTime&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2024-01-01&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SnapshotType&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;manual&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first thing to notice is that we need to set the &lt;code&gt;mock_boto_client.return_value&lt;/code&gt; to &lt;code&gt;mock_boto_client&lt;/code&gt;, which is itself, and this will return the mock &lt;strong&gt;instance&lt;/strong&gt; you are configuring when in &lt;code&gt;MyRDSManager&lt;/code&gt; the code runs &lt;code&gt;self._rds_client = boto3.client("rds")&lt;/code&gt;. If you don't set this MagicMock will return the default value, therefore a &lt;strong&gt;new&lt;/strong&gt; MagicMock, and not what you are configuring! &lt;/p&gt;

&lt;p&gt;Another important point to note is that we patch &lt;code&gt;boto3&lt;/code&gt; within the specific module being tested, rather than the &lt;em&gt;general&lt;/em&gt; &lt;code&gt;boto3&lt;/code&gt; library. In other words, you should patch &lt;code&gt;main.boto3.client&lt;/code&gt;, where &lt;code&gt;main&lt;/code&gt; refers to the module you have written and are currently testing.&lt;/p&gt;

&lt;p&gt;Next, configure the Mock as needed by adding methods and attributes. It's worth noting that setting a &lt;code&gt;side_effect&lt;/code&gt; allows the mock to invoke the function specified in the &lt;code&gt;side_effect&lt;/code&gt;, passing along all the arguments that the code supplies to the mock. &lt;/p&gt;

&lt;p&gt;With this setup, you should be able to fulfil the initial requirements and therefore stub the original behaviour and use all the Mock-provided features.&lt;/p&gt;

&lt;p&gt;Hope this will help you all! &lt;/p&gt;

&lt;p&gt;Any feedback is appreciated. &lt;/p&gt;

&lt;p&gt;Cheers &lt;/p&gt;

&lt;p&gt;Post published also on Virtuability's website &lt;a href="https://www.virtuability.com/blog/2024-04-24-boto3-and-python-unittest-mock/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>boto</category>
      <category>python</category>
    </item>
    <item>
      <title>Golden rules to live well in ITOps</title>
      <dc:creator>Danilo Desole</dc:creator>
      <pubDate>Thu, 30 Nov 2023 14:26:08 +0000</pubDate>
      <link>https://dev.to/panilo/golden-rules-to-live-well-in-itops-22ke</link>
      <guid>https://dev.to/panilo/golden-rules-to-live-well-in-itops-22ke</guid>
      <description>&lt;p&gt;Here I list some simple rules to live well in IT Operations, from my experience; these rules are to be taken as they are, with no warranty, and exceptions can apply.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. No releases on Fridays
&lt;/h2&gt;

&lt;p&gt;Friday is sacred and the weekend is spent with family, friends, or even just chilling out. Do not jeopardize your weekend with a Friday release. &lt;/p&gt;

&lt;h2&gt;
  
  
  2. Production is sacred
&lt;/h2&gt;

&lt;p&gt;Do not touch the production environment, even if the option seems compelling. Copy the environment somewhere else, safe, and play with it. You would spend the same amount of time for copying the environment, to fix possible outages in production caused by your actions. &lt;/p&gt;

&lt;h2&gt;
  
  
  3. Make Team decisions and foster communication
&lt;/h2&gt;

&lt;p&gt;Do not decide for yourself, always involve the team in your final decisions. Speak with your Team and with stakeholders, communication is at the base of early civilization and can be the game changer even nowadays.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. In case of outages, focus on production
&lt;/h2&gt;

&lt;p&gt;In case of outages focus on fixing the production environment, lower environments (dev, stage, etc...) can wait, but customers do not. &lt;/p&gt;

&lt;h2&gt;
  
  
  5. Do not overdo, and do not always follow your gut
&lt;/h2&gt;

&lt;p&gt;Do what you are asked to do, and do not overdo tasks; this is not to be confused with "do the minimal acceptable", but is just a foster to follow your task guardrails and not do things that can lead to issues. For me following my gut sometimes leads to issues, so I also learned how to ignore it :) &lt;/p&gt;

</description>
    </item>
    <item>
      <title>AWS NLB and Client IP Preservation - How to create Security Group Rules</title>
      <dc:creator>Danilo Desole</dc:creator>
      <pubDate>Mon, 23 Oct 2023 09:08:50 +0000</pubDate>
      <link>https://dev.to/panilo/aws-nlb-and-client-ip-perseverance-how-to-create-security-group-rules-2771</link>
      <guid>https://dev.to/panilo/aws-nlb-and-client-ip-perseverance-how-to-create-security-group-rules-2771</guid>
      <description>&lt;p&gt;Have you ever created a Network Load Balancer on AWS, its target group and its target, and some security rules attached to it, to end up giving out to your browser because you couldn't reach the target?&lt;/p&gt;

&lt;p&gt;It happened to me last week and I interestingly found out about this cool AWS feature named Client IP Preservation. &lt;/p&gt;

&lt;p&gt;Client IP Preservation is a target group feature that allows the traffic from the Network Load Balancer to preserve the client IP. Therefore the traffic to your target will keep the IP from the original client. Disabling this option will overwrite the traffic IP with the NLB private IP (the NLB is likely to have more than one IP, it will override the traffic IP with the IP of the ENI that handles the traffic). &lt;/p&gt;

&lt;p&gt;By default, AWS enables client IP preservation if the Target Group type is one of the following&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;EC2 instance (ASG, EC2 instances)&lt;/li&gt;
&lt;li&gt;IP type, protocol UDP or TCP_UDP &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This means that the only case Client IP Preservation is OFF by default is if your target type is IP and the protocol is TCP or TLS. &lt;/p&gt;

&lt;p&gt;But what does this mean? &lt;/p&gt;

&lt;p&gt;It means that you need to remember to allow internet public cidrs in your security group if your target is: one or more EC2 instances or any other target with type IP and protocol UDP or TCP_UDP. &lt;/p&gt;

&lt;p&gt;Instead, if your target is of type IP and the protocol is TCP or TLS, you can simply allow traffic from the NLB private IPs. For example, if you set an ECS cluster as the target of your NLB, its tasks' security group can only contain rules that allow the NLB private IPs.&lt;/p&gt;

&lt;p&gt;Is it any better to enable Client IP Preservation?&lt;/p&gt;

&lt;p&gt;For target types IP + UDP/TCP_UDP and Instances (except some instance types), you cannot disable Client IP Preservation; you can though enable Client IP Preservation for target type IP + TCP/TLS. You should enable Client IP Preservation considering the following &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Targets must be in the same VPC as the Network Load Balancer, and traffic must flow directly from the Network Load Balancer to the target;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Client IP preservation is not supported when using a Gateway Load Balancer endpoint to inspect traffic between the Network Load Balancer and the target;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Client IP preservation is not supported when a target group contains AWS PrivateLink ENIs or the ENI of another Network Load Balancer.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Client IP preservation has no effect on traffic converted from IPv6 to IPv4.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;When you specify targets by Application Load Balancer type, the client IP of all incoming traffic is preserved by the Network Load Balancer and is sent to the Application Load Balancer.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I hope this solution proves helpful to you! &lt;br&gt;
Any feedback is much appreciated! &lt;br&gt;
Enjoy&lt;/p&gt;

&lt;p&gt;Post published also on Virtuability's website &lt;a href="https://www.virtuability.com/blog/2023-10-23-aws-nlb-and-client-ip-preservation/"&gt;here&lt;/a&gt;.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>WordPress Flamingo GDPR</title>
      <dc:creator>Danilo Desole</dc:creator>
      <pubDate>Wed, 14 Jun 2023 14:48:00 +0000</pubDate>
      <link>https://dev.to/panilo/wordpress-flamingo-gdpr-pfp</link>
      <guid>https://dev.to/panilo/wordpress-flamingo-gdpr-pfp</guid>
      <description>&lt;p&gt;The Flamingo WordPress plugin allows you to keep track of Contact Form events. It's an excellent tool for logging every sending and creating a "history" of emails sent over time.&lt;/p&gt;

&lt;p&gt;The only drawback is that the plugin is not GDPR compliant and lacks a feature for deleting old records. &lt;/p&gt;

&lt;p&gt;To address this issue, I created a plugin that automatically cleans up data on your WordPress site after seven days to ensure GDPR compliance. You can find the plugin &lt;a href="https://github.com/Webbame/flamingo_gdpr"&gt;here&lt;/a&gt;, it deletes &lt;code&gt;flamingo_inbound&lt;/code&gt; (contact form) records older than 7 days, and all the &lt;code&gt;flamingo_contact&lt;/code&gt; (emails). &lt;/p&gt;

&lt;p&gt;I hope this solution proves helpful to you! &lt;br&gt;
Enjoy&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Break the rules of virtualization, build Lambdas container images for any platform, from any platform, with CDK &amp; Docker buildx</title>
      <dc:creator>Danilo Desole</dc:creator>
      <pubDate>Tue, 09 May 2023 08:10:55 +0000</pubDate>
      <link>https://dev.to/panilo/how-to-build-lambda-container-image-with-cdk-for-platform-different-than-your-local-1j9a</link>
      <guid>https://dev.to/panilo/how-to-build-lambda-container-image-with-cdk-for-platform-different-than-your-local-1j9a</guid>
      <description>&lt;p&gt;How often are you deploying a Lambda container image, basically a Lambda running on a Docker image, for a platform that doesn't match your localhost platform? Often I deploy functions running on ARM rather than on X86_64, this is a personal preference and it doesn't come with any massive advantage (there are online some comparison), and although my laptop is ARM-based, the CI/CD server is not :/ &lt;/p&gt;

&lt;p&gt;This is true even for GitHub Workflows which run on X86_64, rather than ARM if you don't create your custom runner. &lt;/p&gt;

&lt;p&gt;So if you have a piece of code like the following, and you try to deploy it via a CI/CD server which is X86_64 it will fail with the error&lt;/p&gt;

&lt;p&gt;The error&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Warning: The requested image&lt;span class="s1"&gt;'s platform (linux/arm64/v8) does not match the detected host platform (linux/amd64) and no specific platform was requested

exec /bin/sh: exec format error
The command '&lt;/span&gt;/bin/sh &lt;span class="nt"&gt;-c&lt;/span&gt; ...&lt;span class="s1"&gt;' returned a non-zero code: 1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The code&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;aws_cdk&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;    
    &lt;span class="n"&gt;Stack&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;aws_lambda&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;aws_ecr_assets&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;ecr&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;constructs&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Construct&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;LambdaMultiplatDemStack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Stack&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Construct&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;construct_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;super&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;construct_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;my_function&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;aws_lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;DockerImageFunction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;MyDifferentPlatformFn&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;aws_lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DockerImageCode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_image_asset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
              &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;architecture&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;aws_lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Architecture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ARM_64&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Dockerfile&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; public.ecr.aws/lambda/python:3.8&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; requirements.txt  .&lt;/span&gt;

&lt;span class="k"&gt;RUN  &lt;/span&gt;pip3 &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt &lt;span class="nt"&gt;--target&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;LAMBDA_TASK_ROOT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; my_py_app.py ${LAMBDA_TASK_ROOT}&lt;/span&gt;

&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; [ "my_py_app.handler" ]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is true even for the simplest Docker image because Docker is pulling executable dependencies for your it that are not compatible with your execution platform. How to solve this issue? Thanks to Docker and CDK! &lt;/p&gt;

&lt;h2&gt;
  
  
  Let's start with Docker
&lt;/h2&gt;

&lt;p&gt;Is not news that Docker offers the possibility to build images for a platform different than yours using &lt;code&gt;buildx&lt;/code&gt;, to find out more just visit the official documentation &lt;a href="https://docs.docker.com/build/architecture/"&gt;link&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;So first thing first, we need to check if &lt;code&gt;buildx&lt;/code&gt; is installed on our machine; you can follow the official doc &lt;a href="https://docs.docker.com/engine/install/"&gt;here&lt;/a&gt;, but if you want to simplify the process&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Run &lt;code&gt;docker buildx ls&lt;/code&gt; to list all the builders on your machine, if the output presents something similar to what is below you should be good (see the various platform available)
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;[&lt;/span&gt;panilo@fedora ~]&lt;span class="nv"&gt;$ &lt;/span&gt;docker buildx &lt;span class="nb"&gt;ls 
&lt;/span&gt;NAME/NODE DRIVER/ENDPOINT STATUS  BUILDKIT PLATFORMS
default &lt;span class="k"&gt;*&lt;/span&gt; docker                           
  default default         running 23.0.1   linux/arm64, linux/amd64, linux/amd64/v2, linux/riscv64, linux/ppc64le, linux/s390x, linux/386, linux/mips64le, linux/mips64
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;If the output doesn't present the platform you're interested in, you need to 

&lt;ul&gt;
&lt;li&gt;run the following command &lt;code&gt;docker run --privileged --rm tonistiigi/binfmt --install all&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;run again the &lt;code&gt;docker buildx ls&lt;/code&gt; command, this time the platforms available should be more&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now we can build images for other platforms, great!&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: CDK
&lt;/h2&gt;

&lt;p&gt;Just modify the stack to add a special &lt;code&gt;platform&lt;/code&gt; property in the &lt;code&gt;aws_lambda.DockerImageCode.from_image_asset&lt;/code&gt; definition&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;aws_cdk&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;    
    &lt;span class="n"&gt;Stack&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;aws_lambda&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;aws_ecr_assets&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;ecr&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;constructs&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Construct&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;LambdaMultiplatDemStack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Stack&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Construct&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;construct_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;super&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;construct_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;my_function&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;aws_lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;DockerImageFunction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;MyDifferentPlatformFn&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;aws_lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DockerImageCode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_image_asset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
              &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="n"&gt;platform&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ecr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Platform&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LINUX_ARM64&lt;/span&gt; &lt;span class="c1"&gt;# Magic Switch!
&lt;/span&gt;            &lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;architecture&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;aws_lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Architecture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ARM_64&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;By adding this little parameter CDK will know to use Docker's &lt;code&gt;buildx build&lt;/code&gt; command and prepare the image for the specified platform. &lt;/p&gt;

&lt;p&gt;We can now deploy anywhere, building anywhere :) &lt;/p&gt;

&lt;p&gt;Please find a complete example &lt;a href="https://github.com/panilo/multi_platform_lambda"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I hope this helped you, &lt;br&gt;
as usual, any feedback is more than appreciated! &lt;/p&gt;

&lt;p&gt;Cheers&lt;br&gt;
Danilo&lt;/p&gt;

&lt;p&gt;Post published also on Virtuability's website &lt;a href="https://www.virtuability.com/blog/2023-05-09-break-the-rules-of-virtualisation-lambdas/"&gt;here&lt;/a&gt;.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Deploy a static website on AWS for FREE!</title>
      <dc:creator>Danilo Desole</dc:creator>
      <pubDate>Wed, 19 Apr 2023 15:02:10 +0000</pubDate>
      <link>https://dev.to/panilo/deploy-a-static-website-on-aws-for-free-2ia9</link>
      <guid>https://dev.to/panilo/deploy-a-static-website-on-aws-for-free-2ia9</guid>
      <description>&lt;p&gt;Yes, you read it right! It's possible to host a static website, for FREE, on AWS. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Before starting you need a hosted zone in Route53 for the website's domain you're deploying.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To do that we're going to deploy our static files on S3, create a CloudFront distribution, create a valid TLS certificate in ACM, and finally we'll add an alias record in Route53; let's start! &lt;/p&gt;

&lt;p&gt;So first we need to create a CDK app, I'll not take too much time on this as there are tons of articles around the web on how to do that, therefore once you've done it we can start coding! &lt;/p&gt;

&lt;p&gt;We'll divide the app into two stacks &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;One stack will create the TLS certificate;&lt;/li&gt;
&lt;li&gt;The other stack will create an S3 bucket and the CloudFront distribution;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is because we need to create the TLS certificate in the same region as the CloudFront distribution (us-east-1) and for some reason, CDK handles well the CloudFront distribution creation in that region, regardless if the stack defines the region or not, rather than the TLS certificate stack need a region to be specified. &lt;/p&gt;

&lt;p&gt;So let's start by defining the stack for the TLS certificate, we need first to define a &lt;code&gt;cdk_environment&lt;/code&gt; variable which holds info about the environment in which CDK will be executed (region and account number). For the region, we will use &lt;code&gt;us-east-1&lt;/code&gt; so the certificate will be compatible with CloudFront. Then we need to pass to the &lt;code&gt;super&lt;/code&gt; constructor the env variable we just declared, and an attribute &lt;code&gt;cross_region_references=True&lt;/code&gt;, this little one will help us to reference the certificate's ARN in the other stack. &lt;/p&gt;

&lt;p&gt;Everything else is just CDK construct to define the TLS certificate and its validation method (in the stack below is Route 53).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SSLCertificateStack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Stack&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Construct&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;cdk_environment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Environment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;region&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"us-east-1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"CDK_DEFAULT_ACCOUNT"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nb"&gt;super&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s"&gt;"SSLCertificateStack-&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'.'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'-'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;cdk_environment&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;cross_region_references&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;website_hosted_zone&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;HostedZone&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"domain_hosted_zone"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;domain_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;domain&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;certificate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Certificate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"website_certificate"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;domain_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;subject_alternative_names&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s"&gt;"www.&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;domain&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="n"&gt;validation&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;CertificateValidation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_dns&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;website_hosted_zone&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's now define the other stack for S3 and the CloudFront distribution. Again we will need to define a &lt;code&gt;cdk_environment&lt;/code&gt; variable which will hold the info on where to deploy the resources defined in the stack, including the website assets. In this scenario, we deploy in a different region than before, &lt;code&gt;eu-west-1&lt;/code&gt;. Again we need to pass the magic &lt;code&gt;cross_region_references=True&lt;/code&gt; to get the output from the precedent stack.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;WebsiteStack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Stack&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Construct&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tls_certificate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Certificate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;website_assets_path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;cdk_environment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Environment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;region&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"eu-west-1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"CDK_DEFAULT_ACCOUNT"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="nb"&gt;super&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s"&gt;"WebsiteDeploy-&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'.'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'-'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;cdk_environment&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;cross_region_references&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;domains&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s"&gt;"www.&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;domain&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

        &lt;span class="n"&gt;website_bucket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Bucket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"website_bucket"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;access_control&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;BucketAccessControl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PRIVATE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;encryption&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;BucketEncryption&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;S3_MANAGED&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;website_bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;apply_removal_policy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RemovalPolicy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DESTROY&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;BucketDeployment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"website_deployment"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;destination_bucket&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;website_bucket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;sources&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;asset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;website_assets_path&lt;/span&gt;&lt;span class="p"&gt;)],&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;oai&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;OriginAccessIdentity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"origin_access_identity"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;oai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;apply_removal_policy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RemovalPolicy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DESTROY&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;website_bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;grant_read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;identity&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;oai&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;distribution&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Distribution&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"website_distribution"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;default_root_object&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"index.html"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;default_behavior&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;BehaviorOptions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;origin&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;S3Origin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;website_bucket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;origin_access_identity&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;oai&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="n"&gt;viewer_protocol_policy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ViewerProtocolPolicy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;REDIRECT_TO_HTTPS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;domain_names&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;domains&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;certificate&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;tls_certificate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;CfnOutput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"distribution_url"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;distribution&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;domain_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;export_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s"&gt;"DistributionDomainName-&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'.'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'-'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;CfnOutput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"bucket_arn"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;website_bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bucket_arn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;export_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s"&gt;"WebsiteBuckerARN-&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'.'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'-'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notes of attention are &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;an OAC (Origin Access Control) for S3 which allow requests through the CloudFront distribution&lt;/li&gt;
&lt;li&gt;the CloudFront distribution Viewer Protocol Policy to redirect all the requests to HTTPs&lt;/li&gt;
&lt;li&gt;the two outputs which can be used to test the website deployment (see &lt;code&gt;distribution_url&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now left to do is add two records in Route53, both pointing to the CloudFront distribution. For instance, say we're deploying example.com, we can add: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;example.com&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;www.example.com&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now, if you want to take a look at the whole code, here is the &lt;a href="https://github.com/panilo/cdk_static_website_deployment"&gt;repo&lt;/a&gt; :) &lt;/p&gt;

&lt;h2&gt;
  
  
  What is free
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;S3 offers the first 5 GB of data for free, if you have less than 5 GB around S3 it should be good. More info &lt;a href="https://aws.amazon.com/s3/pricing/"&gt;here&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;CloudFront distribution offers the first tera of data out for free, see &lt;a href="https://aws.amazon.com/cloudfront/pricing/"&gt;here&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;AWS ACM TLS certificates are completely free, see &lt;a href="https://aws.amazon.com/certificate-manager/pricing/"&gt;here&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The only service you pay for is Route 53, min .50 cents per month (see &lt;a href="https://aws.amazon.com/route53/pricing/"&gt;here&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Happy to answer any questions, please let me know if you'd like to see more pictures and if this article is clear :)&lt;/p&gt;

&lt;p&gt;Cheers&lt;br&gt;
Danilo&lt;/p&gt;

</description>
      <category>aws</category>
      <category>staticwebsite</category>
      <category>webdev</category>
      <category>iac</category>
    </item>
    <item>
      <title>AWS CDK + API Gateway and Integrations. A little guide how to.</title>
      <dc:creator>Danilo Desole</dc:creator>
      <pubDate>Tue, 14 Feb 2023 16:19:32 +0000</pubDate>
      <link>https://dev.to/panilo/aws-cdk-api-gateway-and-integrations-a-little-guide-how-to-hdc</link>
      <guid>https://dev.to/panilo/aws-cdk-api-gateway-and-integrations-a-little-guide-how-to-hdc</guid>
      <description>&lt;p&gt;I've been working with CDK and I think is brilliant, the way it let you define resources and infrastructure using your fav coding language is awesome, I personally use Python. Though sometimes what happens is that CDK takes over a lot of control and creates resources as it thinks is proper... Also, documentation lacks some advanced configuration.&lt;/p&gt;

&lt;p&gt;For instance, I come across this &lt;a href="https://docs.aws.amazon.com/apigateway/latest/developerguide/integrating-api-with-aws-services-s3.html" rel="noopener noreferrer"&gt;AWS documentation page&lt;/a&gt; to develop a proxy to S3 using API Gateway. I thought to reproduce the same in CDK, but I faced some issues in defining the integrations. &lt;/p&gt;

&lt;p&gt;In this post, I'm going to show how I created the API Gateway, the challenges I faced, and how I solved them, with the hope it might help you too! &lt;/p&gt;

&lt;p&gt;So let's start from the beginning. &lt;/p&gt;

&lt;h2&gt;
  
  
  Defining REST APIs
&lt;/h2&gt;

&lt;p&gt;To define a REST API you can use the following snippet, in a single row, in CDK, you can define a new set of REST API&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;

&lt;span class="n"&gt;my_apig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;apig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;RestApi&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ApigAndS3&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


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

&lt;/div&gt;
&lt;h2&gt;
  
  
  Defining Resources
&lt;/h2&gt;

&lt;p&gt;Now it's time to define a couple of resources, to be clear our final goal is to create something like the following: &lt;code&gt;http://myapigateway/bucketName/item&lt;/code&gt; where &lt;em&gt;bucketName&lt;/em&gt; and &lt;em&gt;item&lt;/em&gt; are placeholder the client will pass along with a request body. Whatever is passed in the body will be saved in &lt;em&gt;bucketName/item&lt;/em&gt;. Of course, you need to own write permissions on the bucket defined by &lt;em&gt;bucketName&lt;/em&gt;. &lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;

&lt;span class="n"&gt;bucket_resource&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;my_apig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_resource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{bucketName}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;item_resource&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bucket_resource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_resource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{item}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Here again a couple of lines defining two resources. Awesome! &lt;/p&gt;

&lt;h2&gt;
  
  
  Defining the integration
&lt;/h2&gt;

&lt;p&gt;Here I started to sweat, as defining the integrations per se is not complicated &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;

&lt;span class="n"&gt;bucket_integration&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;apig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;AwsIntegration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;s3&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;integration_http_method&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;PUT&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{bucket}/{object}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;region&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;eu-west-1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;integration_options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Is the &lt;em&gt;integration_options&lt;/em&gt; really hard to define, considering I got the error &lt;em&gt;apigateway Invalid mapping expression parameter specified: method.request.path.&lt;/em&gt; many, many, times... I couldn't find an answer online and even the AWS Post &lt;a href="https://aws.amazon.com/premiumsupport/knowledge-center/api-gateway-proxy-path-parameter-error/" rel="noopener noreferrer"&gt;here&lt;/a&gt; added confusion. &lt;/p&gt;

&lt;p&gt;First: what are integration options? Well in integration options you can define how API Gateway will integrate with the defined service, in particular, you can map URL/Body parameters with parameters for your integration. What we aim to do is to use the URL path parameters &lt;em&gt;bucketName/item&lt;/em&gt; to define the S3 path where to save the request body payload. &lt;/p&gt;

&lt;p&gt;In the AWS console, you do something like this, very far from what you do in CDK&lt;/p&gt;

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

&lt;p&gt;Instead in CDK, you will define something like &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;

&lt;span class="n"&gt;integration_response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;apig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;IntegrationResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;200&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;integration_options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;IntegrationOptions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request_parameters&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;integration.request.path.bucket&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;method.request.path.bucketName&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;          &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;integration.request.path.object&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;method.request.path.item&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;credentials_role&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;role&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
&lt;span class="n"&gt;integration_responses&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;integration_response&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;As you can notice the correct path URL to be passed are &lt;em&gt;integration.request.path.bucket&lt;/em&gt; and &lt;em&gt;integration.request.path.object&lt;/em&gt; mapped to &lt;em&gt;method.request.path.bucketName&lt;/em&gt; and &lt;em&gt;method.request.path.item&lt;/em&gt;. &lt;/p&gt;

&lt;p&gt;What really helped me is the CLI, commands &lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;

&lt;p&gt;&lt;span class="n"&gt;aws&lt;/span&gt; &lt;span class="n"&gt;apigateway&lt;/span&gt; &lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;integration&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;rest&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="n"&gt;abcd&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="mi"&gt;1234&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt; &lt;span class="n"&gt;POST&lt;/span&gt;&lt;br&gt;
&lt;span class="n"&gt;aws&lt;/span&gt; &lt;span class="n"&gt;apigateway&lt;/span&gt; &lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;rest&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="n"&gt;abcd&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="mi"&gt;1234&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt; &lt;span class="n"&gt;POST&lt;/span&gt;&lt;/p&gt;

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

&lt;/div&gt;
&lt;h2&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  Final thoughts&lt;br&gt;
&lt;/h2&gt;

&lt;p&gt;CDK is really cool, the documentation needs to be expanded to care also for advanced scenarios, and be extensive to describe all you can actually do. &lt;/p&gt;

&lt;p&gt;Use the CLI when in doubt... This always helps.&lt;/p&gt;

&lt;p&gt;The full code for this mini-POC can be found &lt;a href="https://github.com/panilo/APIG-S3" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Happy to answer any questions, please let me know if you'd like to see more pictures and if this article is clear :)&lt;/p&gt;

&lt;p&gt;Cheers&lt;br&gt;
Danilo&lt;/p&gt;

&lt;p&gt;Post published also on Virtuability's website &lt;a href="https://www.virtuability.com/blog/2023-02-14-aws-cdk-api-gateway-ingreations/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>cdk</category>
      <category>aws</category>
    </item>
    <item>
      <title>How to virtualise on Apple M1</title>
      <dc:creator>Danilo Desole</dc:creator>
      <pubDate>Tue, 17 Jan 2023 11:57:00 +0000</pubDate>
      <link>https://dev.to/panilo/how-to-virtualise-on-apple-m1-1082</link>
      <guid>https://dev.to/panilo/how-to-virtualise-on-apple-m1-1082</guid>
      <description>&lt;p&gt;I recently got a new MacBook Pro backed by the "almost" new Apple M1 socket, it's just brilliant and the battery life it's just astonishing, I'm able to run multiple workloads, have a always-on Fedora VM, and the battery won't die before the second day. &lt;/p&gt;

&lt;p&gt;I usually run a separated environment for my development, this allows me to keep my machine pristine (I used to do that with my previous laptop, Dell XPS 15) from dev dependencies and files that I'll never remove. In this article I'd like to show you how I virtualise on Mac and how I use VM to develop. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Please note that I'm not affiliated with any of the software/vendor below. What I'm writing is from my own experience. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Virtualisation Options
&lt;/h2&gt;

&lt;h3&gt;
  
  
  UTM
&lt;/h3&gt;

&lt;p&gt;UTM is an Open Source virtualisation software able to run Linux, Windows and OSX even on Apple M1. UTM allows you to run the virtualisation as-is or to use Rosetta and emulate an intel-like processor. I really like the UI simplicity and the software itself is just made to do what's supposed to, without too many features that sometime can be overwhelm.  &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;PRO&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Free / paid for 12€ on mac store&lt;/li&gt;
&lt;li&gt;Same features as Parallels (as much as I’m concerned) &lt;/li&gt;
&lt;li&gt;Open Source&lt;/li&gt;
&lt;li&gt;Offer different types of virtualisation (even with Rosetta)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;CONS&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sometimes it can be challenging to create a new VM (the install process may fail if virtualisation is not set correctly)&lt;/li&gt;
&lt;li&gt;Need at least an additional settings screen to setup the IP ranges for the created VMs&lt;/li&gt;
&lt;li&gt;Installing Windows is really challenging as you need to basically create the ISO file (from a Windows machine...)&lt;/li&gt;
&lt;li&gt;Graphic acceleration doesn't work well on Windows &lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Parallels
&lt;/h3&gt;

&lt;p&gt;Parallels is a top-tier emulation software, the VM creation process is straightforward with a Wizard that guide you through all the steps, many automated. It provides a lot of customisations and options to literally unleash the real virtualisation power. This is my fav if you use virtualisation for business reason as you get extensive support with the business version. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;PRO&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It is straightforward and intuitive&lt;/li&gt;
&lt;li&gt;Creation process easy&lt;/li&gt;
&lt;li&gt;Mapping of CMD to CTRL&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;CONS&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;License is annual and expensive &lt;/li&gt;
&lt;li&gt;Doesn’t offer that much more rather than UTM (for my daily usage)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  VSCode
&lt;/h2&gt;

&lt;p&gt;Now VSCode allows you to SSH into a Linux VM, then it installs a VSCode-Server and let you code on the connected machine as you were in local. Additionally, to my surprise, it also automatically set port-forwarding, therefore if you start a web-server it will automatically forward your &lt;code&gt;localhost:something&lt;/code&gt; to you &lt;code&gt;ssh-machine:something&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;What I've done is&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;I created an SSH key and installed it on my VM &lt;/li&gt;
&lt;li&gt;I set a static IP so it won't change on reboot &lt;/li&gt;
&lt;li&gt;Open VSCode and install the Remote SSH extension&lt;/li&gt;
&lt;li&gt;Connect to the VM&lt;/li&gt;
&lt;li&gt;Happy coding!&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Happy to answer any question, please let me know if you'd like to see more pictures and if this article is clear :) &lt;/p&gt;

&lt;p&gt;Special thanks to &lt;a href="https://github.com/mjvirt" rel="noopener noreferrer"&gt;Morten&lt;/a&gt; for his tips and tricks.&lt;/p&gt;

&lt;p&gt;Cheers&lt;br&gt;
Danilo&lt;/p&gt;

</description>
      <category>watercooler</category>
    </item>
    <item>
      <title>Cookiebot and Slider Revoltion</title>
      <dc:creator>Danilo Desole</dc:creator>
      <pubDate>Wed, 02 Jun 2021 08:51:30 +0000</pubDate>
      <link>https://dev.to/panilo/cookiebot-and-slider-revoltion-59io</link>
      <guid>https://dev.to/panilo/cookiebot-and-slider-revoltion-59io</guid>
      <description>&lt;p&gt;Recently I noticed an issue in my WordPress website: I had configured the CookieBot plugin, by CookieBot, to automatically block all the cookies until the user accept/reject those (more info &lt;a href="https://support.cookiebot.com/hc/en-us/articles/360009141959-Automatic-or-Manual-cookie-blocking-mode-which-one-am-I-using-"&gt;here&lt;/a&gt;), and the slider (on all pages) has stopped to work. &lt;/p&gt;

&lt;p&gt;The slider I'm mentioning is Slider Revolution by ThemePunch. &lt;/p&gt;

&lt;p&gt;I struggled to came up with a solution, online the only proposed fix was to set the CookieBot plugin to manually block all the cookies, though using this way I needed to code a bunch of JS in order to effectively block the cookies from being downloaded to the user browser storage. Too lazy to do that! &lt;/p&gt;

&lt;p&gt;So I investigate a little bit and I figured out that Google Fonts doesn't set cookie, but it's collecting IPs and other info (more comprehensive description &lt;a href="https://developers.google.com/fonts/faq#what_does_using_the_google_fonts_api_mean_for_the_privacy_of_my_users"&gt;here&lt;/a&gt;) hence CookieBot is blocking the script execution until the user explicitly accepts that its identity (IP and other stuff mentioned before) can be recorded by Big G.&lt;/p&gt;

&lt;p&gt;Bottom of the bottle: disable Google Fonts load in Slider Revolution and load them as a separate resource, if your theme doesn't load them already. &lt;/p&gt;

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

&lt;p&gt;Happy Blogging!&lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>gdpr</category>
      <category>cookiebot</category>
      <category>privacy</category>
    </item>
    <item>
      <title>Develop and debug a WordPress plugin from the scratch</title>
      <dc:creator>Danilo Desole</dc:creator>
      <pubDate>Wed, 28 Apr 2021 14:05:01 +0000</pubDate>
      <link>https://dev.to/panilo/develop-and-debug-a-wordpress-plugin-from-the-scratch-4lgm</link>
      <guid>https://dev.to/panilo/develop-and-debug-a-wordpress-plugin-from-the-scratch-4lgm</guid>
      <description>&lt;p&gt;I had never been a PHP fan, I born as a C# developer and honestly is quite a fun and easy life with all the IDE and tools doing most of the stuff automagically. &lt;/p&gt;

&lt;p&gt;When I started with PHP many years ago, one of the main difficulty I found was not being able to debug properly and that always affected my development decisions. &lt;/p&gt;

&lt;p&gt;Recently I decided to get back on PHP and specifically I was interested in developing a WordPress plugin. &lt;/p&gt;

&lt;p&gt;Here I want to share with you what I've learned and done in order to debug and properly create a simple WordPress plugin from the scratch. &lt;/p&gt;

&lt;p&gt;We will use Laragon and VS Code.&lt;/p&gt;

&lt;p&gt;Let's start :) &lt;/p&gt;

&lt;h1&gt;
  
  
  Install Laragon and WordPress on the local machine
&lt;/h1&gt;

&lt;p&gt;The first thing to do is to install a web server and add a WordPress project. I used Laragon as the web server. &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Download and install &lt;a href="https://laragon.org/" rel="noopener noreferrer"&gt;Laragon&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Download &lt;a href="http://wordpress.org/" rel="noopener noreferrer"&gt;WordPress&lt;/a&gt; and extract it in the Laragon project folder (e.g. C:\laragon\www) &lt;/li&gt;
&lt;li&gt;Start Laragon&lt;/li&gt;
&lt;li&gt;Laragon in automatic will add to the host file a reference to your project as {project_name}.test (project_name is the folder name)
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In my case I extracted WordPress under a folder named &lt;code&gt;wp&lt;/code&gt; (e.g. C:\laragon\www\wp)&lt;/p&gt;

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

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

&lt;h1&gt;
  
  
  Install PHP
&lt;/h1&gt;

&lt;p&gt;Then we can install PHP, before doing so check the PHP version installed with Laragon, you can verify that by going in the Laragon installation folder then &lt;code&gt;\bin\php&lt;/code&gt;, here you will find a folder named like &lt;code&gt;php-7.2.19-Win32-VC15-x64&lt;/code&gt; in this case &lt;code&gt;7.2.19&lt;/code&gt; is the version. &lt;/p&gt;

&lt;p&gt;We can now go to the official &lt;a href="http://www.php.net/" rel="noopener noreferrer"&gt;PHP website&lt;/a&gt; and download the same version as Laragon, be careful and download the Thread Safe PHP version. &lt;/p&gt;

&lt;p&gt;Once the download ends, extract the content of the archive in a folder on your disk, I use &lt;code&gt;C:\libs&lt;/code&gt; for all the libraries I download. &lt;/p&gt;

&lt;p&gt;You need to add the path where you’ll extract PHP to the environment path (user environment path).  &lt;/p&gt;

&lt;h1&gt;
  
  
  Configure VSCode to properly debug PHP
&lt;/h1&gt;

&lt;p&gt;Open VS code, go to settings and check the following values&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;php.validate.enable&lt;/code&gt; true&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;php.validate.executablePath&lt;/code&gt; e.g. C:\libs\php\php.exe&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;php.validate.run&lt;/code&gt; onSave&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Then install the following extensions&lt;/p&gt;

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

&lt;h1&gt;
  
  
  Install xDebug
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Debugging CLI PHP Scripts
&lt;/h2&gt;

&lt;p&gt;To install xDebug first we need to gather the PHP information for our local environment, to do that run &lt;code&gt;php.exe -i &amp;gt; output&lt;/code&gt; this will print out, in the output file, all the info we need to proceed with the installation. &lt;/p&gt;

&lt;p&gt;Go to &lt;a href="//xdebug.org/wizard"&gt;xdebug.org/wizard&lt;/a&gt; and paste the PHP information stored in the output file, the wizard will inform you on what’re the next steps. &lt;/p&gt;

&lt;p&gt;In my scenario I’ve &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Download php_xdebug-3.0.4-7.4-vc15-x86_64.dll&lt;/li&gt;
&lt;li&gt;Move the downloaded file to C:\php\ext (this might different, check your installation folder)&lt;/li&gt;
&lt;li&gt;Create &lt;code&gt;php.ini&lt;/code&gt; in the same folder as where &lt;code&gt;php.exe&lt;/code&gt; is and add the line &lt;code&gt;zend_extension = C:\php\ext\php_xdebug-3.0.4-7.4-vc15-x86_64.dll&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Pretty straightforward ;) &lt;/p&gt;

&lt;h2&gt;
  
  
  Enable remote debugging
&lt;/h2&gt;

&lt;p&gt;In the &lt;code&gt;php.ini&lt;/code&gt; file, you just placed the &lt;code&gt;zend_extension&lt;/code&gt; configuration, add the following lines&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;xdebug.mode = debug
xdebug.start_with_request = yes
xdebug.client_port = 9000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Debugging Laragon projects
&lt;/h2&gt;

&lt;p&gt;Because we will debug projects deployed via Laragon we need to repeat the above configuration steps for the PHP executable installed under the Laragon folder, it’s store in the &lt;code&gt;bin&lt;/code&gt; folder under the Laragon installation folder.&lt;/p&gt;

&lt;h1&gt;
  
  
  Test all together with a simple script
&lt;/h1&gt;

&lt;p&gt;To test all together I’ll script a simple PHP program to output &lt;em&gt;Hello World&lt;/em&gt;, what a fantasy eh! The goal is to run and debug the script. &lt;/p&gt;

&lt;p&gt;Create a folder, open it in VS Code and create a simple PHP script. The folder part is important we’ll need to create a launch configuration file &lt;code&gt;launch.json&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;?php
  echo "Hello World";
  echo "end";
  end();
?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we can configure the debug options. Hit the debug icon, click &lt;em&gt;create a launch.json file&lt;/em&gt; and select PHP, this will create a launch configuration file with two configurations &lt;em&gt;Listen for Xdebug&lt;/em&gt; and &lt;em&gt;Launch currently open script&lt;/em&gt;. Let’s see what these two configurations mean&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Listen for Xdebug: this config will start listening on the specified port (usually xDebug uses port 9000). Every time you make a request with a browser to your webserver or launch a CLI script, xDebug will connect and VS code will stop on breakpoints, exceptions, etc…&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Launch currently open script: this config is an example of CLI debugging, it will load and start the current script in CLI, showing all the stdout/stderr output in the debug console. The debug session will be automatically closed once the script ends. &lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In the end, we should see two debug configurations, in this very case we need to use the second &lt;em&gt;Launch currently open script&lt;/em&gt;.&lt;/p&gt;

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

&lt;p&gt;Done :) &lt;/p&gt;

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

&lt;h1&gt;
  
  
  Test all together with WordPress
&lt;/h1&gt;

&lt;p&gt;Now let’s take a look at how we can debug a WordPress plugin, is intuitive that we need to use the &lt;em&gt;Listen for Xdebug&lt;/em&gt; debug configuration to do that. &lt;/p&gt;

&lt;p&gt;The simplest plugin we could try to debug is &lt;code&gt;hello dolly&lt;/code&gt;, a built-in WordPress plugin. &lt;/p&gt;

&lt;p&gt;I’ll start by launching my Laragon servers, this will spawn up WordPress I previously installed as a project.&lt;/p&gt;

&lt;p&gt;Then I’ll go to my &lt;code&gt;WordPress project folder/wp-content/plugins&lt;/code&gt; and open the &lt;code&gt;hello.php&lt;/code&gt; file in VS Code, this is the &lt;code&gt;hello-dolly&lt;/code&gt; plugin let’s try to debug it. &lt;/p&gt;

&lt;p&gt;In VS Code go to the debug tab and create a new configuration, then click on &lt;em&gt;Listen to Xdebug&lt;/em&gt;, for test reason place a breakpoint in the &lt;code&gt;hello_dolly_get_lyric()&lt;/code&gt; return statement (line 49).&lt;/p&gt;

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

&lt;p&gt;Now open a browser and navigate to your WordPress wp-admin service, as you log in the breakpoint should be hit, that is the confirmation our debug process works. &lt;/p&gt;

&lt;p&gt;Finally, we can take care of &lt;code&gt;folder-mapping&lt;/code&gt; this particular feature will allow us to store and work on the code not directly placed under our WordPress folder, this is crucial to avoid issues like deleting your own code or overwrite the plugin with outdated files. &lt;/p&gt;

&lt;p&gt;Let’s copy the &lt;code&gt;hello.php&lt;/code&gt; file in a folder somewhere else, could be your desktop, open VS Code and prepare a debug configuration, this time we need to add a little twist to map our PHP file to the one deployed server-side (on another folder on our PC, consider the procedure is valid also for a remote server if that server allows PHP debug).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  // Use IntelliSense to learn about possible attributes.
  // Hover to view descriptions of existing attributes.
  // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Listen for Xdebug",
      "type": "php",
      "request": "launch",
      "port": 9000,
      "pathMappings": {
        "C:\\laragon\\www\\wp\\wp-content\\plugins": "${workspaceFolder}"
      }
    },
    {
      "name": "Launch currently open script",
      "type": "php",
      "request": "launch",
      "program": "${file}",
      "cwd": "${fileDirname}",
      "port": 9000
    }
  ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The lines from 12 to 14 describe how VS code should map the current workspace code to the server deployed code. &lt;/p&gt;

&lt;p&gt;Now if you run again the debug, you got to your WP instance in the wp-admin section, you should be able to debug. Consider some errors that might occur when you hit the debug button, this is because in your folder there’re no WP files and Laragon needs to load them; if that happens just start the debug session again and you should be grand. You'll be also able to deeply debug WP files. &lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusions
&lt;/h1&gt;

&lt;p&gt;Honestly, I found PHP development and debug pretty easy, I remember nightmares and high difficulty in my past it might be me, improving my skills, or better tools on the market. &lt;/p&gt;

&lt;p&gt;Let me know what you think about this article, is my first one on this community and I really appreciate your feedback to improve. &lt;/p&gt;

&lt;p&gt;See you soon in my next article of WordPress Pluging Developement Series.&lt;/p&gt;

&lt;p&gt;Cheers ✌&lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>programming</category>
      <category>php</category>
      <category>vscode</category>
    </item>
  </channel>
</rss>
