<?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: Mike Sanders</title>
    <description>The latest articles on DEV Community by Mike Sanders (@msanders5).</description>
    <link>https://dev.to/msanders5</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%2F1508286%2F7b1e0b22-f910-4b1c-8df6-56e8a81d4cee.JPEG</url>
      <title>DEV Community: Mike Sanders</title>
      <link>https://dev.to/msanders5</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/msanders5"/>
    <language>en</language>
    <item>
      <title>Ansible playbooks for the Linux minimalist</title>
      <dc:creator>Mike Sanders</dc:creator>
      <pubDate>Sat, 31 Jan 2026 20:31:01 +0000</pubDate>
      <link>https://dev.to/msanders5/ansible-playbooks-for-the-linux-minimalist-4ke3</link>
      <guid>https://dev.to/msanders5/ansible-playbooks-for-the-linux-minimalist-4ke3</guid>
      <description>&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;I am sharing these playbooks because they have helped me manage virtual machines in my home lab and cloud instances. Users should have a basic knowledge of Ansible, some experience with Linux, and some familiarity with virtual machines. You can follow along with the repository at Git Hub &lt;a href="https://github.com/devdog66/ansible-playbooks" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Environment
&lt;/h2&gt;

&lt;p&gt;These playbooks were written with the following environment in place. You will need to adjust files and playbooks to meet your environment's needs.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Local network of 10.20.26.x&lt;/li&gt;
&lt;li&gt;Working Bind/DNS service managing the home.internal domain&lt;/li&gt;
&lt;li&gt;Working email service for the above domain&lt;/li&gt;
&lt;li&gt;Working HashiCorp Vault instance to manage secrets&lt;/li&gt;
&lt;li&gt;Debian 12 workstation to run the Ansible playbooks&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Ansible Setup
&lt;/h2&gt;

&lt;p&gt;To install Ansible and its dependencies on Debian 12 run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo apt install ansible
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, clone this &lt;a href="https://github.com/devdog66/ansible-playbooks" rel="noopener noreferrer"&gt;repository&lt;/a&gt; and change to the cloned directory. The next command will create an Ansible Vault file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ansible-vault create secrets.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will ask you for an Ansible Vault password and then open &lt;code&gt;secrets.yaml&lt;/code&gt; in a text editor. If you plan to use HashiCorp Vault to manage passwords, you can add your token to &lt;code&gt;secrets.yaml&lt;/code&gt; here. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fd6qb9pwrj49ishhgf0aw.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fd6qb9pwrj49ishhgf0aw.jpg" alt="Text editing of secrets.yaml" width="780" height="332"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Otherwise you can add your sudo passwords to &lt;code&gt;secrets.yaml&lt;/code&gt; for Ansible Vault to manage.&lt;/p&gt;

&lt;p&gt;If using HashiCorp Vault, you will also need to install the &lt;code&gt;hvac&lt;/code&gt; Python package via &lt;code&gt;pip&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;sudo apt install python3-pip
python3 -m venv myenv
source myenv/bin/activate
pip install hvac
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you are using &lt;code&gt;vault.yaml&lt;/code&gt;, it will connect to your HashiCorp Vault instance. Edit the URL to match your Vault instance's URL. &lt;code&gt;vault.yaml&lt;/code&gt; is treated as a vars file; it pulls keys from a HashiCorp Vault path. For this example the path is &lt;code&gt;kv/data/ansible&lt;/code&gt;. It uses the Ansible secret named &lt;code&gt;ansible_token&lt;/code&gt; to store the token for HashiCorp Vault.&lt;/p&gt;

&lt;p&gt;The next file to edit is the &lt;code&gt;inventory.yaml&lt;/code&gt; file. This example shows how to pull secrets from both HashiCorp Vault and Ansible Vault. For example, the &lt;code&gt;ansible_become_password&lt;/code&gt; for &lt;code&gt;dev.devsrv1&lt;/code&gt; is stored in Ansible Vault. The &lt;code&gt;ansible_become_password&lt;/code&gt; for &lt;code&gt;network.debiansrv1&lt;/code&gt; is stored in HashiCorp Vault and returned in the key list called &lt;code&gt;vault_data&lt;/code&gt;, under the key &lt;code&gt;debiansrv1_sudo&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Flhws7aiapb3amn39dofa.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Flhws7aiapb3amn39dofa.jpg" alt="Inventory.yaml snapshot" width="690" height="379"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The last step is to generate SSH key pairs if you don't already have them. This public key will be used to communicate with the servers that Ansible will manage.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ssh-keygen
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Minimal Debian VM Setup
&lt;/h2&gt;

&lt;p&gt;On to creating VM templates. The first template will be a fresh install of Debian 12. I changed the VM networking to use bridged mode. This places the VM on the local network using DHCP.&lt;/p&gt;

&lt;p&gt;For a minimal install, deselect all options in Software Selection except SSH Server, since this is needed to manage the machine via Ansible.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fm7yhovk8huerrrf645qn.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fm7yhovk8huerrrf645qn.jpg" alt="Debian Software selection screen" width="791" height="588"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After the instance is installed, stop it and clone the instance for further work. I call this template &lt;code&gt;deb12base&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Preparing cloned template for Ansible
&lt;/h2&gt;

&lt;p&gt;On a clean Debian 12 install, you cannot SSH in as root. Therefore, &lt;code&gt;sudo&lt;/code&gt; and &lt;code&gt;python3&lt;/code&gt; need to be installed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;apt install sudo python3
/usr/sbin/usermod -a -G sudo &amp;lt;sudouser&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, edit &lt;code&gt;/etc/network/interfaces&lt;/code&gt; to give the template a static IP on the network. Add the host to DNS so you can use the hostname in the inventory file. There is a playbook for that: &lt;code&gt;bind-update.yaml&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you want to change the template hostname, use the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo hostnamectl set-hostname &amp;lt;hostname&amp;gt;.home.internal
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To allow Ansible playbooks to connect, copy your SSH public key to the new VM:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ssh-copy-id &amp;lt;sudouser&amp;gt;@&amp;lt;hostname&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After these changes, stop the instance and clone it for further work. I call this template &lt;code&gt;deb12work&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ansible Playbook usage
&lt;/h2&gt;

&lt;p&gt;Now, on to using the Ansible playbooks.&lt;/p&gt;

&lt;p&gt;The first Ansible playbook to run sets the system clock to use &lt;code&gt;systemd-timesyncd&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;ansible-playbook --ask-vault-pass clock-setup.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The second playbook sets up an email client so services or cron jobs can notify a network user via email. This playbook copies a couple of files from the &lt;code&gt;assets&lt;/code&gt; folder to the server. These files should be reviewed and edited before running the playbook.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ansible-playbook --ask-vault-pass mail-setup.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Test the email setup by sending an email from the guest machine.&lt;/p&gt;

&lt;p&gt;The next playbook installs &lt;code&gt;rkhunter&lt;/code&gt; and ClamAV. This script installs cron jobs to run these tools daily. The cron jobs also run an apt upgrade check and email the results of what packages need to be installed without actually installing them. Be sure to edit these so they notify the correct email address. The playbook will also copy a stricter &lt;code&gt;/etc/ssh/sshd_config&lt;/code&gt; to prevent root logins and enforce safer TLS settings. &lt;code&gt;rkhunter&lt;/code&gt; will complain if these are not set.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ansible-playbook --ask-vault-pass security-tools-install.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This playbook will restart the server or guest instance.&lt;/p&gt;

&lt;p&gt;Next playbook will use nftables as a firewall. It isn't very strict as it allows all outbound traffic. It does block inbound traffic except for SSH. Adjust it according to your needs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ansible-playbook --ask-vault-pass nftables-install.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next playbook will install and configure fail2ban. This utility slows down attackers by putting them in a jail for a period of time. Adjust the configurations to your preference.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ansible-playbook --ask-vault-pass fail2ban-install.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Apt Upgrades
&lt;/h2&gt;

&lt;p&gt;This playbook will upgrade apt packages on multiple servers. Configure as needed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ansible-playbook --ask-vault-pass apt-upgrade.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Install Nginx Server
&lt;/h2&gt;

&lt;p&gt;This playbook will install nginx and compile it with modsecurity. It will add modsecurity rules from OWASP. Also, it will add jails to fail2ban for modsecurity and for various 400 errors over periods of time to prevent system scanning. The playbook has a variable for the version of nginx you are using. Be sure to change this to the correct version you are targeting.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ansible-playbook --ask-vault-pass nginx-install.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Shutdown and Restart
&lt;/h2&gt;

&lt;p&gt;These playbooks can simply restart or shutdown a group of instances.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ansible-playbook --ask-vault-pass restart.yaml
ansible-playbook --ask-vault-pass shutdown.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Change Hostname
&lt;/h2&gt;

&lt;p&gt;When cloning new instances from templates, you might want to change the hostname for the new clone. This playbook will copy /etc/network/interfaces so you can set the static IP as well. Make sure to edit that before running. Here is the playbook call for that. This playbook shows how to pass environment variables to ansible.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ansible-playbook --ask-vault-pass change-hostname.yaml -e 'hostname=devsrv1'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Finalizing templates by regenerating host keys
&lt;/h2&gt;

&lt;p&gt;Up until now, I have been cloning templates and reusing those templates. This is fine except for the fact that SSH host keys have been reused as well. For a final step I want to regenerate the SSH host keys and finalize a VM template to be re-used. This will cause Man in the Middle warnings the next time you try to connect, so you will have to follow the instructions to clear those up. I found this technique at this address, &lt;a href="https://www.youtube.com/watch?v=oAULun3KNaI" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?v=oAULun3KNaI&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ansible-playbook --ask-vault-pass prep-template.yaml 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>ansible</category>
      <category>linux</category>
      <category>virtualmachine</category>
      <category>minimalist</category>
    </item>
    <item>
      <title>A Custom Consent Management approach for GDPR compliance</title>
      <dc:creator>Mike Sanders</dc:creator>
      <pubDate>Sat, 19 Jul 2025 20:33:47 +0000</pubDate>
      <link>https://dev.to/msanders5/a-custom-consent-management-approach-for-gdpr-compliance-56fo</link>
      <guid>https://dev.to/msanders5/a-custom-consent-management-approach-for-gdpr-compliance-56fo</guid>
      <description>&lt;p&gt;The purpose of this post is to illustrate how GDPR implementations can satisfy the following requirements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Provide a default non-script way for a user to opt-in or revoke consent based on category of cookies.&lt;/li&gt;
&lt;li&gt;Redirect to a JavaScript implementation for the same when JavaScript is enabled.&lt;/li&gt;
&lt;li&gt;Have the implementation work for both dotnet core and dotnet framework front ends.&lt;/li&gt;
&lt;li&gt;The consent cookie should be protected and not accessible by JavaScript.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  User Experience
&lt;/h2&gt;

&lt;p&gt;A simple web template is used to illustrate the user experience. &lt;br&gt;
The template has a simple header and footer with a body that &lt;br&gt;
contains a consent banner at the bottom of the page. &lt;br&gt;
The banner has a link to the privacy policy and a button to accept cookies. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2F0cq5kqmpe37tcamrw2xg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2F0cq5kqmpe37tcamrw2xg.png" alt="Simple web template" width="800" height="468"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After clicking the accept button, the user is redirected to a page &lt;br&gt;
that shows that the consent has been accepted.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fcs4avyh7fimzlardh5lp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fcs4avyh7fimzlardh5lp.png" alt="Consent acceptance" width="800" height="182"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The user can manage consent by cookie category by clicking a link in the footer. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fn1catyxeqgbz4nnozarw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fn1catyxeqgbz4nnozarw.png" alt="Manage cookies link" width="800" height="456"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Manage Cookies link defaults to a simple non-JavaScript UI. By default any unnecessary categories are not selected, using an Opt-In strategy.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fwfto3it0aeiftx1gf7b5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fwfto3it0aeiftx1gf7b5.png" alt="Simple cookie categories user interface" width="800" height="252"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If the user selects the marketing category, then the ads will be &lt;br&gt;
shown on the pages. The user can deselect marketing cookies and &lt;br&gt;
the ads will be removed from the pages.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2F8l7nokb0dc8i6b81bleh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2F8l7nokb0dc8i6b81bleh.png" alt="Enable site ads" width="800" height="229"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Solution architecture
&lt;/h2&gt;

&lt;p&gt;The code for the solution is located on &lt;a href="https://github.com/devdog66/gdpr-solution" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;The solution is illustrated in this diagram. The dotted lines with arrows denote dependency.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2F9tooyd6ho31qxr5x5et8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2F9tooyd6ho31qxr5x5et8.png" alt="solution architecture" width="800" height="541"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The central project to fulfill implementation for both dotnet core and dotnet framework clients is WebUtils.Standard, which is coded with .NET Standard 2.0. This library contains the service client for the Consent Service. That service along with the domain project &lt;br&gt;
are coded with dotnet core 8.&lt;/p&gt;

&lt;p&gt;Another item to note are the facades created around HTTP Context, Request, and Response objects. I passed in these facades to the WebUtils.Standard so that both .NET Framework and dotnet core clients&lt;br&gt;
can use that library. The implementations for the facades are in the &lt;br&gt;
WebUtils.Frmwk and WebUtils.Core libraries respectively.&lt;/p&gt;

&lt;p&gt;In addition to the non-Javascript user interface, I have began a VueJS implementation of opting into cookie categories that exists in the Web.Core project. This implementation is only a starting point.&lt;/p&gt;

&lt;p&gt;For the database, I chose Mysql/MariaDB as the implementation. That can be easily swapped out. &lt;/p&gt;

&lt;h2&gt;
  
  
  Test Coverage
&lt;/h2&gt;

&lt;p&gt;There is a little room for improvement for test coverage, but not too bad for now.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fkzgdrjfr07mow47icndb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fkzgdrjfr07mow47icndb.png" alt=" " width="747" height="293"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrap up
&lt;/h2&gt;

&lt;p&gt;I hope you enjoy this small template project and the code is something you can learn something from. &lt;/p&gt;

</description>
      <category>webdev</category>
      <category>gdpr</category>
      <category>dotnetcore</category>
      <category>dotnetframework</category>
    </item>
    <item>
      <title>Journey into new Web Tech</title>
      <dc:creator>Mike Sanders</dc:creator>
      <pubDate>Thu, 20 Jun 2024 19:07:18 +0000</pubDate>
      <link>https://dev.to/msanders5/journey-into-new-web-tech-2o5o</link>
      <guid>https://dev.to/msanders5/journey-into-new-web-tech-2o5o</guid>
      <description>&lt;p&gt;Today's web offers many new technologies, the core of which is to employ a Content Management tool that the site owner can edit their content. From a development perspective, the next steps would be to pull that content, code the client side scripts and then finally to serve it as a web application.&lt;/p&gt;

&lt;p&gt;While there are many tools to accomplish the above, the remainder of this post will outline the tools I chose to develop a site. Another goal is to re-use such work for creating any web applications for prospective clients. For content management, I decided upon Ghost CMS and for Static Site generation, I decided upon Astro.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ghost CMS
&lt;/h2&gt;

&lt;p&gt;Besides the installation located at &lt;a href="https://ghost.org/docs/install/" rel="noopener noreferrer"&gt;https://ghost.org/docs/install/&lt;/a&gt;, the next step with using Ghost CMS, would be to create or use a theme. This has a learning curve to it if creating a new theme. Ghost themes are written using &lt;a href="https://handlebarsjs.com/" rel="noopener noreferrer"&gt;Handlebars&lt;/a&gt;, another templating language to learn if you have not already done so. Most of the existing themes I have looked at also use gulp to concatenate the CSS files. Ghost has some pretty good &lt;a href="https://ghost.org/docs/themes/" rel="noopener noreferrer"&gt;documentation on creating themes&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I have a working theme that I uploaded to &lt;a href="https://github.com/devdog66/meikos-ghost-theme" rel="noopener noreferrer"&gt;git hub&lt;/a&gt; so that the reader can follow along for the rest of the post. &lt;/p&gt;

&lt;p&gt;A good practice that I have seen many ghost theme authors do, is to separate concerns meaning to have separate CSS files for certain areas or components of a site. An example of how I did this is below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2F5ynapwesiflfhjzhu61s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2F5ynapwesiflfhjzhu61s.png" alt=" " width="304" height="443"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One CSS file, I chose to name it main.css, imports the other CSS files that gulp will concatenate and minify. Here is how my main.css file looks.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2F3my13gh7pf3iwb24evko.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2F3my13gh7pf3iwb24evko.png" alt=" " width="417" height="279"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Some may find some limitations with Ghost about theming. One such limitation would be with navigation links. Out of the box, there are no multi-level capabilities nor are there ways to assign icons to links if desired. Therefore, I made the choice to hard code the navigation for a site using some placeholders. Another limitation is the seemingly lack of ability to preview the theme. I have tried the &lt;a href="https://www.npmjs.com/package/express-handlebars" rel="noopener noreferrer"&gt;express-handlebars&lt;/a&gt; package to do this, however ran into roadblocks with Ghost specific variables and statements. I may revisit that attempt later. Right now the only way I see to preview is to zip up the theme files and upload to the Ghost admin location. Then you can preview it on Ghost.&lt;/p&gt;

&lt;p&gt;The project is fairly simple with the following npm steps outlined below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fw49xfg3qimfmijqb4820.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fw49xfg3qimfmijqb4820.png" alt=" " width="506" height="132"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The first step is a utility from Ghost called gscan. This allows you to check the syntax of your .hbs files and the theme overall to make sure it adheres to a valid Ghost theme. Next is the build step that gulp will use to pack the main.css file. Now to create the theme zip file with the npm zip script. It uses gulp again to do this. I chose not to build with this step as sometimes the errors that gscan produces are not detailed enough. If one uploads the zipped theme even with errors, the Ghost admin site will provide more details as to what specifically needs to be fixed.&lt;/p&gt;

&lt;p&gt;A final optional step is to copy the theme or assets folder to the next project in Astro. Here is the example of the main site template, default.hbs. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Ff3lbfq5xxjaba1hsbyhm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Ff3lbfq5xxjaba1hsbyhm.png" alt=" " width="735" height="614"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  Astro SSG
&lt;/h2&gt;

&lt;p&gt;As of now, I have a Ghost instance with a theme of my liking and I have created the content on my Ghost instance. My next goal is to pull the content from Ghost CMS and to create the static assets needed for a web site. This is where Astro comes in. I have uploaded another repo to &lt;a href="https://github.com/devdog66/meikos-astro" rel="noopener noreferrer"&gt;git hub&lt;/a&gt; for the user to follow along with. &lt;/p&gt;

&lt;p&gt;The project structure looks like the following.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fx9x1k8hmg6tkltg2xubg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fx9x1k8hmg6tkltg2xubg.png" alt=" " width="320" height="471"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The public folder mostly contains the assets sub folder copied from the ghost theme. The src folder is broken down into the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;components = &lt;em&gt;Reusable parts to be included in layouts or pages.&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;content    = &lt;em&gt;The folder where blogs and pages will be imported to. This follows Astro's content collections instructions.&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;importer   = &lt;em&gt;This has the script used for importing content.&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;layouts    = &lt;em&gt;This has the main layout for the site. Can include multiple layouts if desired.&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;pages      = &lt;em&gt;This contains slugs to render pages or posts.&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The meat of the project is with the importer/index.ts script. It uses an API that needs to be setup in the Ghost instance with an API Key. In order to get Astro and Ghost to work together nicely, some workarounds were needed for this step.&lt;/p&gt;

&lt;p&gt;Ghost exports it's format as HTML. Astro is built for Markdown. Therefore the exported content should be stored as .MDX files to work. Next workaround is with the fact that Ghost does not always export well-formed HTML and therefore I was forced to use a tool like sanitize-html to take care of that. I had to use it like below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2F4h45fgc99k8esirvc0t8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2F4h45fgc99k8esirvc0t8.png" alt=" " width="644" height="147"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The layout is pretty simple and mirrors what was used with the default layout for the Ghost theme.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2F59citetpddv19lj4hgp8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2F59citetpddv19lj4hgp8.png" alt=" " width="764" height="594"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The page slug looks like the following and shows how to use the 'page' content collection. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fmfy6n0w4ykia2imjogyk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fmfy6n0w4ykia2imjogyk.png" alt=" " width="800" height="482"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;All of this results in a beautiful cover page for a new web site.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2F2m4ie9yrzjzrc0djwv2a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2F2m4ie9yrzjzrc0djwv2a.png" alt=" " width="800" height="487"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And there is a list of recent blog posts, including this one. Talk about eating your own dog food.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fk50wpz4lra14kyr5x7e1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fk50wpz4lra14kyr5x7e1.png" alt=" " width="800" height="485"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Astro vs Visual Studio 2022 as Static Site Generators</title>
      <dc:creator>Mike Sanders</dc:creator>
      <pubDate>Thu, 23 May 2024 02:32:27 +0000</pubDate>
      <link>https://dev.to/msanders5/astro-vs-visual-studio-2022-as-static-site-generators-32nd</link>
      <guid>https://dev.to/msanders5/astro-vs-visual-studio-2022-as-static-site-generators-32nd</guid>
      <description>&lt;p&gt;I have spent today learning the new Astro SSG. You can read about it at &lt;a href="https://astro.build/" rel="noopener noreferrer"&gt;https://astro.build/&lt;/a&gt; or better yet you can get started with it at &lt;a href="https://docs.astro.build/en/install/auto/" rel="noopener noreferrer"&gt;https://docs.astro.build/en/install/auto/&lt;/a&gt;. While Visual Studio isn't exactly a SSG, they way you code with it can make static web pages. VS has so much more power than that. &lt;/p&gt;

&lt;h2&gt;
  
  
  What is a Static Site Generator?
&lt;/h2&gt;

&lt;p&gt;Honestly, the term is sort of new to me. But basically, it means a tool that creates static web pages. What are those? Web pages that only need html, css, and if desired javascript. The official definition of course is at Wikipedia, &lt;a href="https://en.wikipedia.org/wiki/Static_site_generator" rel="noopener noreferrer"&gt;https://en.wikipedia.org/wiki/Static_site_generator&lt;/a&gt;. &lt;/p&gt;

&lt;h2&gt;
  
  
  Setup
&lt;/h2&gt;

&lt;p&gt;So, for the Astro side, I used the following&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;VS Code (latest)&lt;/li&gt;
&lt;li&gt;Debian 11 OS w 16GB Ram&lt;/li&gt;
&lt;li&gt;LxQt &lt;/li&gt;
&lt;li&gt;node 18.6&lt;/li&gt;
&lt;li&gt;astro 9.5.1&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For Visual Studio, of course I need&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Windows 11 w 32GB Ram&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Comparisons
&lt;/h2&gt;

&lt;h3&gt;
  
  
  IDE Performance
&lt;/h3&gt;

&lt;p&gt;Obviously VS Code is going to win this hands down.&lt;/p&gt;

&lt;h3&gt;
  
  
  Intellisense
&lt;/h3&gt;

&lt;p&gt;Astro -- The intellisense for Astro related code is not too bad using the Astro extension in VSCode, but after adding Vue to the mix, I was a bit disappointed that some Vue code did not show up as missing variable errors. However, the hot swap debug environment with Astro did point out those errors. &lt;/p&gt;

&lt;p&gt;Visual Studio -- When it came to choosing CSS classes, Visual Studio still knocks it out of the park for adding classes to elements. VS knows exactly what classes are available and gives very quick dropdowns to this affect. VS wins the intellisense compare.&lt;/p&gt;

&lt;h3&gt;
  
  
  Flexibility
&lt;/h3&gt;

&lt;p&gt;This one I don't think you can compare. This is where each tool has it's purposes and both do excellent jobs towards that. I won't go into Visual Studio, but I would like to highlight some integration points that Astro offers.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CSS -- Tailwind&lt;/li&gt;
&lt;li&gt;WebWorkers -- Partytown (have not been there in a while, but I do like the song)&lt;/li&gt;
&lt;li&gt;UI/Javascript Frameworks -- React, Svelte, Vue, plus a few more&lt;/li&gt;
&lt;li&gt;SSR -- Cloudfare, Vercel, Node, Netlify&lt;/li&gt;
&lt;li&gt;Headless CMS Integration-- Too many to mention, but many are in development. &lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  My Take
&lt;/h2&gt;

&lt;p&gt;I will always opt for a strong backend tool to serve up static pages, like dotnet core. The question and goal I have is how to create a static site quickly and without having to refactor too much later. I might be in a place where I don't worry about styling the site too much and get the content done using Astro. This is where Astro's integration with headless cms systems could have a huge advantage. Plus I think from a templating and component perspective, developing with Astro will be a lot quicker. After all of that is done, then I might let Visual Studio take over for the styling of the sites pages. &lt;/p&gt;

&lt;p&gt;Either way, I think it was a day well spent on learning the Astro SSG tool. It is a very promising project in the node/npm space. &lt;/p&gt;

</description>
      <category>compare</category>
      <category>static</category>
      <category>site</category>
      <category>ide</category>
    </item>
    <item>
      <title>A Command line memorable password generator. Now in Python.</title>
      <dc:creator>Mike Sanders</dc:creator>
      <pubDate>Tue, 21 May 2024 03:39:06 +0000</pubDate>
      <link>https://dev.to/msanders5/a-command-line-memorable-password-generator-now-in-python-kcj</link>
      <guid>https://dev.to/msanders5/a-command-line-memorable-password-generator-now-in-python-kcj</guid>
      <description>&lt;p&gt;I have been using this utility for a couple of years and thought I would share it, code and all. A few years back, I saw an article about best practices for passwords using a combination of a couple of words, numbers, and symbols. At the time, I had a Mac machine that had a nice generator, but I thought...&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;How can I make this usable across all operating systems?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Python to the rescue
&lt;/h2&gt;

&lt;p&gt;Python is one of those languages that is easy to install on all Operating Systems. It basically is already installed on many, and for developers, it is a nice language to have around.&lt;/p&gt;

&lt;p&gt;The script is pretty straightforward. It uses a word dictionary from Linux operating systems and randomly picks a couple of words, numbers, and symbols and creates a list of passwords for you to choose from. &lt;/p&gt;

&lt;h2&gt;
  
  
  Show Me the Passwords
&lt;/h2&gt;

&lt;p&gt;Okay, so the script is going to ask what password length you want. It can generate passwords 8 to 36 characters long and yes, you can remember these! &lt;/p&gt;

&lt;p&gt;Here is example output for 16 character passwords.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;aerial3543?regal
baling58+Creator
hones34464%avert
rebind54%Sukkoth
halting8!gannets
inroads68^wattle
toehold5%salving
zenith91+curbing
crime859~Antaeus
bingo5375$rapist
brogan791-texted
dolled16*kindled
kismet86#leering
wrack87728*probe
prose521+prevail
bubble761~onward
pinups69=gristle
inkiest4$uniting
deftly131+Kansas
Orbison6?Nicobar
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is some craziness with 36 character long passwords.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;uncharacteristic162-characterization
segregationists9881,departmentalized
anthropocentric29567%deconstructions
agriculturalists7195-procrastinators
instantaneously88827^counterexamples
incompatibility1227+aristocratically
telecommunication8.anesthesiologists
indeterminately7818^circumscriptions
exemplification44961!authoritatively
counterclaiming52167*inaccessibility
industrialization96%nationalizations
septuagenarians78367%familiarization
obstructionists18361*inconsequential
instrumentation67833$uncommunicative
interchangeably23734#representatives
environmentalists134-radiotelephones
parliamentarians4914,particularities
disillusionment5514&amp;amp;acknowledgements
maintainability18449*exemplification
trustworthiness86634.consciousnesses
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Show me the code
&lt;/h2&gt;

&lt;p&gt;Sure, it's up on Git Hub at &lt;a href="https://github.com/devdog66/memorable-password" rel="noopener noreferrer"&gt;https://github.com/devdog66/memorable-password&lt;/a&gt;&lt;/p&gt;

</description>
      <category>passwords</category>
      <category>memorable</category>
      <category>generator</category>
      <category>python</category>
    </item>
  </channel>
</rss>
