<?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: Owen Davies</title>
    <description>The latest articles on DEV Community by Owen Davies (@owendavies).</description>
    <link>https://dev.to/owendavies</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%2F329747%2Fe5121200-6c34-4c4d-ac2b-30bb4cdf3143.jpg</url>
      <title>DEV Community: Owen Davies</title>
      <link>https://dev.to/owendavies</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/owendavies"/>
    <language>en</language>
    <item>
      <title>Storing DevOps documentation in Git and managing CAB approvals with Pull Requests</title>
      <dc:creator>Owen Davies</dc:creator>
      <pubDate>Mon, 23 Nov 2020 00:00:00 +0000</pubDate>
      <link>https://dev.to/owendavies/storing-devops-documentation-in-git-and-managing-cab-approvals-with-pull-requests-42a4</link>
      <guid>https://dev.to/owendavies/storing-devops-documentation-in-git-and-managing-cab-approvals-with-pull-requests-42a4</guid>
      <description>&lt;p&gt;Often in a large enterprise you will need to submit changes to a CAB (Change Advisory Board) before getting approval to make any changes to your infrastructure. But managing versions of your diagrams and documents / infrastructure code / what has actually been deployed can be difficult.&lt;/p&gt;

&lt;p&gt;Documentation is often stored in a seperate document store (like Sharepoint).&lt;/p&gt;

&lt;p&gt;I’m using Azure DevOps to manage work items here, but you can insert your own (Jira, Github Issues).&lt;/p&gt;

&lt;h1&gt;
  
  
  Our plan today
&lt;/h1&gt;

&lt;ol&gt;
&lt;li&gt;Problems to address&lt;/li&gt;
&lt;li&gt;Solution&lt;/li&gt;
&lt;li&gt;New document creation process&lt;/li&gt;
&lt;li&gt;Diagram&lt;/li&gt;
&lt;li&gt;Results&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;
  
  
  1. Problems to address
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;Separating documents from infrastructure code often causes documents to be out of date as they’re not always up to date in Sharepoint.&lt;/li&gt;
&lt;li&gt;Decisions of when/why changes were implemented is hard to keep track of.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  2. Solution
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;Store documents within Git (Source Control), directly with infrastructure code.&lt;/li&gt;
&lt;li&gt;Utilise Pull Requests in Azure DevOps for CAB approval of document changes (and track any changes that have been requested to the documents).&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  3. New document creation process
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;Each application folder has a docs folder (with LLDs, HLDs, Visio diagrams, etc)&lt;/li&gt;
&lt;li&gt;An Engineer will have a Story in Azure DevOps to do the analysis and design work and create the document&lt;/li&gt;
&lt;li&gt;They will create a branch to make the changes to the documents in that folder, commit the branch and submit a Pull Request when they are complete.&lt;/li&gt;
&lt;li&gt;When a Pull Request is requested to master, a release pipeline copies the docs folder to the folder in Sharepoint.&lt;/li&gt;
&lt;li&gt;The PR is assigned to a CAB member for CAB.&lt;/li&gt;
&lt;li&gt;They then take this to CAB, and can view the documents in Sharepoint or in the repository.&lt;/li&gt;
&lt;li&gt;If approved this is merged into master, and the new story can be created for the next sprint to do the implementation.&lt;/li&gt;
&lt;li&gt;If denied, they CAB can create bugs against the Pull Request in Azure DevOps, those can be assigned to the engineer who can make the changes and the docs folder will be copied again to the Sharepoint folder&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  4. Diagram
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Aov1V7_E--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/http://www.plantuml.com/plantuml/proxy%3Fcache%3Dno%26src%3Dhttps://raw.githubusercontent.com/Owen-Davies/owen-davies.github.io/master/assets/devops-docs-in-git.puml" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Aov1V7_E--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/http://www.plantuml.com/plantuml/proxy%3Fcache%3Dno%26src%3Dhttps://raw.githubusercontent.com/Owen-Davies/owen-davies.github.io/master/assets/devops-docs-in-git.puml" alt="your-UML-diagram-name"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  5. Results
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;Versioned documents and code are stored together at the same time.&lt;/li&gt;
&lt;li&gt;Any time code is changed you will see if documentation hasn’t been updated and also know what actually changed.&lt;/li&gt;
&lt;li&gt;Part of the Pull Request process for committing code, can be to ensure documentation has been updated if needed.&lt;/li&gt;
&lt;li&gt;All conversations around the document changes will be visible inside the Pull Request. Comments can be seen / discussed inside the Pull Request, and bugs created and assigned for any changes needed. (This will show us approved date, and will see the who/when decisions were made to make changes)&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  6. Conclusion
&lt;/h1&gt;

&lt;p&gt;Now we have documentation and infrastructure code stored together and versioned in the same location.&lt;/p&gt;

&lt;p&gt;Next post will be setting this up in Azure DevOps or maybe GitHub actions.&lt;/p&gt;

&lt;p&gt;What do you think of this approach?&lt;/p&gt;

&lt;p&gt;How do you store your documentation for you infrastructure code?&lt;/p&gt;

</description>
      <category>devops</category>
      <category>git</category>
      <category>azuredevops</category>
      <category>iac</category>
    </item>
    <item>
      <title>Super lightweight bookmarks manager - dmenu bookmarks with bm</title>
      <dc:creator>Owen Davies</dc:creator>
      <pubDate>Tue, 12 May 2020 00:00:00 +0000</pubDate>
      <link>https://dev.to/owendavies/super-lightweight-bookmarks-manager-dmenu-bookmarks-with-bm-1n6i</link>
      <guid>https://dev.to/owendavies/super-lightweight-bookmarks-manager-dmenu-bookmarks-with-bm-1n6i</guid>
      <description>&lt;p&gt;Storing my internet bookmarks to disk in a non-proprietry way and making them easily accessible was my main goal in the research for this blog post.&lt;/p&gt;

&lt;p&gt;A while back I decided that I wanted to TRY to break away from Google in as many ways as I could, not using Chrome as a web browser was one of the biggies. But one of the most convenient things about Chrome is the bookmark sync. The downside to Chrome, is they are tracking everything you do. Whether this is a bad thing or not I leave up to you.&lt;/p&gt;

&lt;p&gt;I tried so many different bookmark managers, but so many of them use databases and require complex setup, or special syncing software running.&lt;/p&gt;

&lt;p&gt;I wanted something that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Stored to disk in a text file&lt;/li&gt;
&lt;li&gt;Let me use my Dropbox / Nextcloud or normal Cloud sync software to take care of syncing between devices&lt;/li&gt;
&lt;li&gt;Works in Chrome / Firefox / Surf / anything&lt;/li&gt;
&lt;li&gt;Easily accessible&lt;/li&gt;
&lt;li&gt;Super lightweight&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I settled on an amazingly simple bookmark manager called bm, and using dmenu (which I was already using as my main menu bar).&lt;/p&gt;

&lt;p&gt;Simply hitting &lt;strong&gt;Super + Shift + B&lt;/strong&gt; brings up the bookmark manager&lt;/p&gt;

&lt;h2&gt;
  
  
  Final result:
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6wAcMDSZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://owendavies.net/assets/images/dmenu-bm/dmenu-bm-lq.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6wAcMDSZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://owendavies.net/assets/images/dmenu-bm/dmenu-bm-lq.gif" alt="dmenu + bm - Bookmark manager annimation"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  The plan
&lt;/h1&gt;

&lt;ol&gt;
&lt;li&gt;What is bm?&lt;/li&gt;
&lt;li&gt;What is dmenu?&lt;/li&gt;
&lt;li&gt;My dmenu-bm script&lt;/li&gt;
&lt;li&gt;Link to the script on github&lt;/li&gt;
&lt;li&gt;Conclusion&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;
  
  
  1. What is bm?
&lt;/h1&gt;

&lt;p&gt;Enter &lt;strong&gt;bm&lt;/strong&gt; for “Simple Bash CLI bookmarks” &lt;a href="https://github.com/tj/bm"&gt;https://github.com/tj/bm&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Format of the bookmarks file
&lt;/h2&gt;

&lt;p&gt;| Each bookmark gets a single line in the text file, and is delimited by |&lt;/p&gt;

&lt;h3&gt;
  
  
  Example:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bd8b3eff7fa82a0382a3e7576c5363b6|2016-01-18T07:21:36Z|:1|https://github.com/tj/bm|bm a cool enhanced bookmark tool for your console|default,shell

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

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;First column is the GUID&lt;/li&gt;
&lt;li&gt;Second column is the DateTime added&lt;/li&gt;
&lt;li&gt;Third column… I’m not sure?&lt;/li&gt;
&lt;li&gt;Fourth column is the URL link&lt;/li&gt;
&lt;li&gt;Five column is the Description text&lt;/li&gt;
&lt;li&gt;Sixth column are the tags comma delimited&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  CLI commands
&lt;/h2&gt;

&lt;p&gt;You can access and manipulate your bookmarks from the CLI. Although I don’t feel this is the most useful way to access it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding bookmarks
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bm add https://www.duckduckgo.com "DuckDuckGo Search Engine" search privacy

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Listing bookmarks
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bm -l

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Searching bookmarks
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bm -s 'string'

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Opening bookmarks
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bm -o 'string'

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  The downside
&lt;/h2&gt;

&lt;p&gt;It doesn’t make it very easily accessible, at least not enough for me. I want to be able to launch a webpage without having to go into a terminal.&lt;/p&gt;

&lt;h1&gt;
  
  
  2. What is dmenu?
&lt;/h1&gt;

&lt;p&gt;dmenu is a super lightweight menu bar for linux.&lt;/p&gt;

&lt;p&gt;dmenu was development by the guys at &lt;a href="https://suckless.org/"&gt;suckless&lt;/a&gt;. They build software “with a focus on simplicity, clarity and frugality.” Basically they create minimal software that is hyper-performant. Sounds cool eh?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://tools.suckless.org/dmenu/"&gt;https://tools.suckless.org/dmenu/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I love dmenu, it’s lightweight, and allows me to customise it with scripts. It’s become my main menu system on my Linux machine. I’m adding more and more scripts to it and optimising my workflow.&lt;/p&gt;

&lt;h1&gt;
  
  
  3. My dmenu bookmarks script
&lt;/h1&gt;

&lt;h2&gt;
  
  
  dmenu-bm.sh
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#!/usr/bin/env bash

declare -A g bmarray;

while IFS=\| read -r guid date id url title tags;
do
  bookmark="$title "-" "$url" "-" "$tags"";
  bmarray["$bookmark"]="$url";
done &amp;lt; /home/owen/Nextcloud/bookmarks/bm.lnk

function load() {
  while IFS=\| read -r guid date id url title tags;
  do
    bookmark="$title "-" "$url" "-" "$tags"";
    printf "$bookmark\n";
  done &amp;lt; /home/owen/Nextcloud/bookmarks/bm.lnk
  printf
}

choice=$(load | dmenu -i -l 15 -p "Add/Open bookmark:")

case "$choice" in
  Add) dmenu-bm-add.sh ;;
  *) bm -o ${bmarray[$choice]} ;;
esac

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  dmenu-bm-add.sh
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#!/bin/sh

result() {
  echo -n | dmenu -p "$1"
}

url="$result "URL:")"
title="$(result "Title:")"
tags="$(result "Tags (comma delimited):")"

bm -b '/home/owen/Nextcloud/bookmarks/bm.lnk' -a $url -T "$title" -t "$tags"

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  My shortcut in dwm
&lt;/h2&gt;

&lt;h3&gt;
  
  
  config.def.h
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;static const char *bookmarkscmd[] = { "dmenu-bm.sh", NULL };

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  TODO:
&lt;/h2&gt;

&lt;p&gt;I still have some tidying of this up, some code duplication in the dmenu-bm.sh script. But for the most part this works, and I use this in my day to day workflow.&lt;/p&gt;

&lt;h1&gt;
  
  
  4. Link to the script on github
&lt;/h1&gt;

&lt;p&gt;You can find the above files in my github at &lt;a href="https://github.com/Owen-Davies/dmenu-bm/"&gt;https://github.com/Owen-Davies/dmenu-bm/&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  5. Conclusion
&lt;/h1&gt;

&lt;p&gt;There we have it, a very simple bookmark management solution that I use day to day.&lt;/p&gt;

&lt;p&gt;I’m a big fan of dmenu, do you have any dmenu scripts that you use that you recommend?&lt;/p&gt;

&lt;p&gt;Still a bit new to bash scripting, so please point me in the direction of any improvements, I will try to revisit this once I get better. But for now it works, so :-)&lt;/p&gt;

&lt;p&gt;You can check out some other dmenu scripts on the subreddit &lt;a href="https://www.reddit.com/r/dmenu/"&gt;https://www.reddit.com/r/dmenu/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>articles</category>
      <category>linux</category>
      <category>dmenu</category>
    </item>
    <item>
      <title>Create an Azure Virtual Machine with Terraform</title>
      <dc:creator>Owen Davies</dc:creator>
      <pubDate>Mon, 24 Feb 2020 00:00:00 +0000</pubDate>
      <link>https://dev.to/owendavies/create-an-azure-virtual-machine-with-terraform-19pm</link>
      <guid>https://dev.to/owendavies/create-an-azure-virtual-machine-with-terraform-19pm</guid>
      <description>&lt;p&gt;To round off things nicely, I thought I would follow on from two previous posts about creating Azure Virtual Machines. First we went through how to create an Azure Virtual Machine using PowerShell, then we went through with ARM templates. Now we’re going to use Terraform for the third try.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://owendavies.net/articles/how-to-create-an-azure-virtual-machine-with-powershell/"&gt;Create an Azure Virtual Machine with PowerShell&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://owendavies.net/articles/how-to-create-an-azure-virtual-machine-with-ARM-template/"&gt;Create an Azure Virtual Machine with an ARM template&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Setting the scene
&lt;/h1&gt;

&lt;p&gt;Let’s assume that we have nothing setup. No virtual network, no storage, nothing. So we will be using Terraform to define everything.&lt;/p&gt;

&lt;h1&gt;
  
  
  The plan
&lt;/h1&gt;

&lt;ol&gt;
&lt;li&gt;Create our terraform file&lt;/li&gt;
&lt;li&gt;Create the AzureRM provider in terraform&lt;/li&gt;
&lt;li&gt;Define the Azure resource group&lt;/li&gt;
&lt;li&gt;Define a virtual network and subnet&lt;/li&gt;
&lt;li&gt;Define a new public IP address&lt;/li&gt;
&lt;li&gt;Define a Network Interface for our VM&lt;/li&gt;
&lt;li&gt;Define the Virtual Machine&lt;/li&gt;
&lt;li&gt;Build the Virtual Machine&lt;/li&gt;
&lt;li&gt;The whole file in one&lt;/li&gt;
&lt;li&gt;Conclusion&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;
  
  
  1. Create the terraform file
&lt;/h1&gt;

&lt;p&gt;Let’s create our terraform file and name it main.tf&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;touch main.tf

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

&lt;/div&gt;



&lt;h1&gt;
  
  
  2. Create the AzureRM provider in terraform
&lt;/h1&gt;

&lt;p&gt;Open up main.tf in your editor of choice and add the azure provider to the top of the file&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;provider "azurerm" {
  version = "= 2.0.0"
  features {}
}

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

&lt;/div&gt;



&lt;h1&gt;
  
  
  3. Define the Azure resource group
&lt;/h1&gt;

&lt;p&gt;Now let’s create our new resource group that everything will live inside&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;resource "azurerm_resource_group" "rg" {
  name = "my-first-terraform-rg"
  location = "northeurope"
}

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

&lt;/div&gt;



&lt;h1&gt;
  
  
  4. Define a virtual network and subnet
&lt;/h1&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;resource "azurerm_virtual_network" "myvnet" {
  name = "my-vnet"
  address_space = ["10.0.0.0/16"]
  location = "northeurope"
  resource_group_name = azurerm_resource_group.rg.name
}

resource "azurerm_subnet" "frontendsubnet" {
  name = "frontendSubnet"
  resource_group_name = azurerm_resource_group.rg.name
  virtual_network_name = azurerm_virtual_network.myvnet.name
  address_prefix = "10.0.1.0/24"
}

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

&lt;/div&gt;



&lt;h1&gt;
  
  
  5. Define a new public IP address
&lt;/h1&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;resource "azurerm_public_ip" "myvm1publicip" {
  name = "pip1"
  location = "northeurope"
  resource_group_name = azurerm_resource_group.rg.name
  allocation_method = "Dynamic"
  sku = "Basic"
}

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

&lt;/div&gt;



&lt;h1&gt;
  
  
  6. Define a Network Interface for our VM
&lt;/h1&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;resource "azurerm_network_interface" "myvm1nic" {
  name = "myvm1-nic"
  location = "northeurope"
  resource_group_name = azurerm_resource_group.rg.name

  ip_configuration {
    name = "ipconfig1"
    subnet_id = azurerm_subnet.frontendsubnet.id
    private_ip_address_allocation = "Dynamic"
    public_ip_address_id = azurerm_public_ip.myvm1publicip.id
  }
}

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

&lt;/div&gt;



&lt;h1&gt;
  
  
  7. Define the Virtual Machine
&lt;/h1&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;resource "azurerm_windows_virtual_machine" "example" {
  name = "myvm1"  
  location = "northeurope"
  resource_group_name = azurerm_resource_group.rg.name
  network_interface_ids = [azurerm_network_interface.myvm1nic.id]
  size = "Standard_B1s"
  admin_username = "adminuser"
  admin_password = "Password123!"

  source_image_reference {
    publisher = "MicrosoftWindowsServer"
    offer = "WindowsServer"
    sku = "2019-Datacenter"
    version = "latest"
  }

  os_disk {
    caching = "ReadWrite"
    storage_account_type = "Standard_LRS"
  }
}

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

&lt;/div&gt;



&lt;h1&gt;
  
  
  8. Build the Virtual Machine
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Login to Azure with the CLI
&lt;/h2&gt;

&lt;p&gt;There are several ways to authenticate with Azure to run our terraform file, for this example I’m going to suggest the Azure CLI&lt;/p&gt;

&lt;p&gt;Make sure you have the Azure CLI installed, then run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;az login

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

&lt;/div&gt;



&lt;p&gt;Which should bring up a browser window for you to login to your Azure subscription.&lt;/p&gt;

&lt;h2&gt;
  
  
  Run terraform init
&lt;/h2&gt;

&lt;p&gt;Now we need to run &lt;strong&gt;terrafrom init&lt;/strong&gt; to prepare the directory and pull down the resources that we have defined in our file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;terraform init

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Build our terraform file
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;terraform apply

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

&lt;/div&gt;



&lt;p&gt;Time taken: 3m10s&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The whole file in one
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;provider "azurerm" {
  version = "= 2.0.0"
  features {}
}

resource "azurerm_resource_group" "rg" {
  name = "my-first-terraform-rg"
  location = "northeurope"
}

resource "azurerm_virtual_network" "myvnet" {
  name = "my-vnet"
  address_space = ["10.0.0.0/16"]
  location = "northeurope"
  resource_group_name = azurerm_resource_group.rg.name
}

resource "azurerm_subnet" "frontendsubnet" {
  name = "frontendSubnet"
  resource_group_name = azurerm_resource_group.rg.name
  virtual_network_name = azurerm_virtual_network.myvnet.name
  address_prefix = "10.0.1.0/24"
}

resource "azurerm_public_ip" "myvm1publicip" {
  name = "pip1"
  location = "northeurope"
  resource_group_name = azurerm_resource_group.rg.name
  allocation_method = "Dynamic"
  sku = "Basic"
}

resource "azurerm_network_interface" "myvm1nic" {
  name = "myvm1-nic"
  location = "northeurope"
  resource_group_name = azurerm_resource_group.rg.name

  ip_configuration {
    name = "ipconfig1"
    subnet_id = azurerm_subnet.frontendsubnet.id
    private_ip_address_allocation = "Dynamic"
    public_ip_address_id = azurerm_public_ip.myvm1publicip.id
  }
}

resource "azurerm_windows_virtual_machine" "example" {
  name = "myvm1"  
  location = "northeurope"
  resource_group_name = azurerm_resource_group.rg.name
  network_interface_ids = [azurerm_network_interface.myvm1nic.id]
  size = "Standard_B1s"
  admin_username = "adminuser"
  admin_password = "Password123!"

  source_image_reference {
    publisher = "MicrosoftWindowsServer"
    offer = "WindowsServer"
    sku = "2019-Datacenter"
    version = "latest"
  }

  os_disk {
    caching = "ReadWrite"
    storage_account_type = "Standard_LRS"
  }
}

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

&lt;/div&gt;



&lt;h1&gt;
  
  
  10. Conclusion
&lt;/h1&gt;

&lt;p&gt;So there we have it, a new Virtual Machine built in Azure using terraform. I personally really like the formatting and syntax compared to ARM templates. Of course I think &lt;strong&gt;terraform plan&lt;/strong&gt; is where the magic is, which ARM template CURRENTLY has no answer for. I’m sure it will come soon enough to ARM, but in the mean time. Terraform really is a great solution for IaC (Infrastructure as Code).&lt;/p&gt;

</description>
      <category>azure</category>
      <category>terraform</category>
    </item>
    <item>
      <title>Terraform - setting up domain DNS with cloudflare</title>
      <dc:creator>Owen Davies</dc:creator>
      <pubDate>Mon, 17 Feb 2020 00:00:00 +0000</pubDate>
      <link>https://dev.to/owendavies/terraform-setting-up-domain-dns-with-cloudflare-bji</link>
      <guid>https://dev.to/owendavies/terraform-setting-up-domain-dns-with-cloudflare-bji</guid>
      <description>&lt;p&gt;I need to verify my domain name with google to confirm that I own it. To do that I need to create a TXT record in the DNS, which is currently hosted in Cloudflare. I’ve been wanting to get my Cloudflare DNS into Terraform for a while, so in this article we’re going to import my existing DNS settings into the Terraform state file, and then add a new TXT record to verify with google.&lt;/p&gt;

&lt;p&gt;If you need to install Terraform check out my article on &lt;a href="https://owendavies.net/articles/getting-started-with-terraform"&gt;Getting Started with Terraform&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Our plan today
&lt;/h1&gt;

&lt;ol&gt;
&lt;li&gt;Setup our new Terraform file&lt;/li&gt;
&lt;li&gt;Get our Cloudflare API key&lt;/li&gt;
&lt;li&gt;(Optional) Import our already existing DNS configuration from Cloudflare&lt;/li&gt;
&lt;li&gt;Create our DNS resources in our Terraform file&lt;/li&gt;
&lt;li&gt;Run our Terraform DNS configuration&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;
  
  
  1. Setup our new Terraform file
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Create our new Terraform file in a new project directory
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir cloudflare-terraform
cd cloudflare-terraform
touch main.tf

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Add the cloudflare provider to our terraform file
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;provider "cloudflare" {
  version = "~&amp;gt; 2.0"
  email = "you@example.com"
  api_key = "your-api-key"
}

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Run terraform init
&lt;/h2&gt;

&lt;p&gt;Run terraform init to setup the folder and download the providers declared in our main.tf terraform file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;terraform init

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

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--E3_p6oOm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://owendavies.net/assets/images/terraform-cloudflare-dns/terraform-init.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--E3_p6oOm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://owendavies.net/assets/images/terraform-cloudflare-dns/terraform-init.png" alt="terraform init output"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can see that terraform has pulled down version 2.3 of the cloudflare provider.&lt;/p&gt;

&lt;h1&gt;
  
  
  2. Get our Cloudflare Global API key
&lt;/h1&gt;

&lt;ol&gt;
&lt;li&gt;Login to Cloudflare with your credentials&lt;/li&gt;
&lt;li&gt;Click on My Profile at the top right&lt;/li&gt;
&lt;li&gt;Click on the 3rd tab “API Tokens”&lt;/li&gt;
&lt;li&gt;Click on View next to Global API Key&lt;/li&gt;
&lt;li&gt;Type in your password again to confirm it’s you&lt;/li&gt;
&lt;li&gt;Copy your API Key and store it somewhere super safe. Don’t give this to anyone that you don’t want to be able to access and modify your DNS.&lt;/li&gt;
&lt;li&gt;Add the generated token to your main.tf file.&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;
  
  
  3. (Optional) Import our already existing DNS configuration from Cloudflare
&lt;/h1&gt;

&lt;h2&gt;
  
  
  1. Add the Zone to our Terraform file
&lt;/h2&gt;

&lt;p&gt;Let’s query the Cloudflare API to get the Zone ID of the domain that we want to import the DNS records for. By querying &lt;strong&gt;&lt;a href="https://api.cloudflare.com/client/v4/zones/"&gt;https://api.cloudflare.com/client/v4/zones/&lt;/a&gt;&lt;/strong&gt; it will output all zones information.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl -X GET "https://api.cloudflare.com/client/v4/zones/" -H "X-Auth-Key:YOUR_API_KEY" -H "X-Auth-Email:you@example.com" -H "Content-Type:application/json" | jq .

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

&lt;/div&gt;



&lt;p&gt;Find the domain (zone) that you want to import and copy the ID value, this is what we will use to import in to the state file.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Create a Zone entry in our Terraform file
&lt;/h2&gt;

&lt;p&gt;Create a new resource with the type &lt;strong&gt;cloudflare_zone&lt;/strong&gt; and name it something appropriate.&lt;/p&gt;

&lt;p&gt;Mine looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;resource "cloudflare_zone" "owendavies-net" {
 zone= "owendavies.net"
}

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  3. Import the zone in to the Terraform state file so that it knows about it.
&lt;/h2&gt;

&lt;p&gt;Run the following command, replacing ZONE_ID with the ID that you copied in the first step.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;terraform import cloudflare_zone.owendavies-net ZONE_ID

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  4. Check the Terraform file is correct
&lt;/h2&gt;

&lt;p&gt;Now let’s run &lt;strong&gt;terraform plan&lt;/strong&gt; and look at the output, if it complains and says that it’s going to make a change, that means that the &lt;strong&gt;cloudflare_zone&lt;/strong&gt; resource you created in your terraform file is different to what terraform imported into the state file, it will tell you what changes there are, modify the resource until it says there are no changes to make when you run &lt;strong&gt;terraform plan&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;terraform plan

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  5. Get DNS records from Cloudflare API
&lt;/h2&gt;

&lt;p&gt;Run the following curl command replacing the YOUR_ZONE_ID and YOUR_API_KEY and email address.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl -X GET "https://api.cloudflare.com/client/v4/zones/YOUR_ZONE_ID/dns_records" -H "X-Auth-Key:YOUR_API_KEY" -H "X-Auth-Email:you@example.com" -H "Content-Type:application/json" | jq . | tee importedcloudflare.json

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

&lt;/div&gt;



&lt;p&gt;This will query the Cloudflare api and return all the DNS records for the ZONE_ID that you specify, it will format it into JSON and save it to &lt;strong&gt;importedcloudflare.json&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Create Terraform resource to match the above output
&lt;/h2&gt;

&lt;p&gt;Open up the &lt;em&gt;importedcloudflare.json&lt;/em&gt; file and create a new cloudflare_record resource matching the values in the &lt;strong&gt;importedcloudflare.json&lt;/strong&gt; file.&lt;/p&gt;

&lt;p&gt;When you have created the resource, find the id value in the &lt;em&gt;importedcloudflare.json&lt;/em&gt; file.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Import the resource
&lt;/h2&gt;

&lt;p&gt;Run the following command (replacing the ZONE_ID &amp;amp; DNS_RECORD_ID with your IDs) to import the resource into the terraform state file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;terraform import cloudflare_record.www-owendavies-net ZONE_ID/DNS_RECORD_ID

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  8. Test to see if the file matches the state
&lt;/h2&gt;

&lt;p&gt;Run &lt;em&gt;terraform plan&lt;/em&gt; which will show you what changes will be run (the differences between you state file and the main.tf file).&lt;/p&gt;

&lt;p&gt;Make sure it says there are “No changes”, if there are any changes that it makes, go back and edit your resource in your main.tf file and try again.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;terraform plan

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  My final terraform file:
&lt;/h2&gt;

&lt;p&gt;In my case I have 2 CNAME records as well as the cloudflare zone itself (the domain).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;provider "cloudflare" {
  version = "~&amp;gt; 2.0"
  email = "you@example.com"
  api_key = "your-api-key"
}

resource "cloudflare_zone" "owendavies-net" {
 zone= "owendavies.net"
}

resource "cloudflare_record" "owendavies-net" {
  zone_id = "ZONE_ID"
  name = "owendavies.net"
  value = "owen-davies.github.io"
  type = "CNAME"
  proxied = true
  ttl = 1
}

resource "cloudflare_record" "www-owendavies-net" {
  zone_id = "ZONE_ID"
  name = "www"
  value = "owen-davies.github.io"
  type = "CNAME"
  proxied = true
  ttl = 1
}

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

&lt;/div&gt;



&lt;p&gt;Now when I run &lt;em&gt;terraform plan&lt;/em&gt; it says there are no changes to be applied&lt;/p&gt;

&lt;h1&gt;
  
  
  4. Create our DNS file in Terraform
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Add a Cloudflare Zone resource
&lt;/h2&gt;

&lt;p&gt;If you didn’t already do this during the (optional) import, you will need a &lt;strong&gt;cloudflare_zone&lt;/strong&gt; resource.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;resource "cloudflare_zone" "example" {
  zone = "example.com"
}

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Add a record to the domain
&lt;/h2&gt;

&lt;p&gt;In my case I’m adding a new TXT record to the domain that google wants to see to verify my domain ownership:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;resource "cloudflare_record" "txt-owendavies-net" {
  zone_id = "ZONE_ID"
  name = "owendavies.net"
  value = "google-site-verification=JZMrzi_LveMVIyP8g5ZICIcPVYg39UPvyOjbLmRLuWU"
  type = "TXT"
  proxied = false 
  ttl = 1

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

&lt;/div&gt;



&lt;h1&gt;
  
  
  5. Run our Terraform DNS configuration
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Check the plan
&lt;/h2&gt;

&lt;p&gt;Run &lt;em&gt;terraform plan&lt;/em&gt; to see the changes that are going to happen&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;terraform plan

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

&lt;/div&gt;



&lt;p&gt;Check the output from the above &lt;strong&gt;terraform plan&lt;/strong&gt; and make sure that all that terraform is going to do is add the new dns record, it shouldn’t be changing anything else if you added the resources correctly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make the changes
&lt;/h2&gt;

&lt;p&gt;Now it’s time toe run &lt;strong&gt;terraform apply&lt;/strong&gt; and make the change in Cloudflare.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;terraform apply

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

&lt;/div&gt;



&lt;p&gt;Hopefully if everything has gone well, you now have your domain in Terraform, and a new record in the dns. You can now commit these changes to source control, and in the future can manage all your changes by Infrastructure as Code (IaC).&lt;/p&gt;

</description>
      <category>cloudflare</category>
      <category>terraform</category>
      <category>devops</category>
    </item>
    <item>
      <title>Using Docker for local SQL Server development</title>
      <dc:creator>Owen Davies</dc:creator>
      <pubDate>Mon, 03 Feb 2020 00:00:00 +0000</pubDate>
      <link>https://dev.to/owendavies/articles-using-docker-for-local-sql-server-development-n2h</link>
      <guid>https://dev.to/owendavies/articles-using-docker-for-local-sql-server-development-n2h</guid>
      <description>&lt;p&gt;Using docker images for development is a super power, you don’t need SQL installed on your development machine, yet you still have the full power of SQL there for you when you need it, and if you want specific versions for different projects you can simply spin up the version you want, and keep all the configuration inside a dockerfile in your repository for that project.&lt;/p&gt;

&lt;p&gt;In this article we are going to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Download the latest SQL Server docker image locally&lt;/li&gt;
&lt;li&gt;Create a docker container and connect to it from SQL Server Management Studio&lt;/li&gt;
&lt;li&gt;Setup a volume mount to store the database files outside of the container so we can persist the data when we tear down our container.&lt;/li&gt;
&lt;li&gt;Set this up with a docker-compose file&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;
  
  
  1. Download the SQL Server docker image
&lt;/h1&gt;

&lt;p&gt;We need to pull down the SQL Server docker image from the container registry with 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;docker pull mcr.microsoft.com/mssql/server

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

&lt;/div&gt;



&lt;p&gt;You should see something similar to the following image:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bZ7UAvFr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://owendavies.net/assets/docker-sqlserver/docker-pull.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bZ7UAvFr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://owendavies.net/assets/docker-sqlserver/docker-pull.png" alt="docker pull mssql server"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  2. Run the container
&lt;/h1&gt;

&lt;p&gt;Now that we have the sql server image locally, it’s time to run the container.&lt;/p&gt;

&lt;p&gt;We need to specify several parameters to do this:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;-e or –env&lt;/strong&gt; - This is where we set the environment variables needed by the container&lt;br&gt;&lt;br&gt;
&lt;strong&gt;-p or –publish&lt;/strong&gt; - This publishes a containers ports to the host ports specified&lt;br&gt;&lt;br&gt;
&lt;strong&gt;–name&lt;/strong&gt; - Sets the name of the container&lt;br&gt;&lt;br&gt;
&lt;strong&gt;-d or –detach&lt;/strong&gt; - Runs the container in the background (detached)&lt;/p&gt;

&lt;p&gt;SQL Server needs us to set 2 environment variables, first to Accept the EULA agreement, and secondly to set the SA password.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker run -e "ACCEPT_EULA=Y" -e "SA_PASSWORD=Sample123$" -p 1433:1433 --name sqlserver -d mcr.microsoft.com/mssql/server:latest

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

&lt;/div&gt;



&lt;p&gt;The output of this looks like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--y7ppunNQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://owendavies.net/assets/docker-sqlserver/docker-run.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--y7ppunNQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://owendavies.net/assets/docker-sqlserver/docker-run.png" alt="docker run"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can see that the command spun up the container and returned the new container ID, then stayed running in the background, leaving our terminal free to run more commands (this is the detached parameter we ran).&lt;/p&gt;

&lt;h2&gt;
  
  
  Check what containers are running
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker container ls

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

&lt;/div&gt;



&lt;p&gt;As you can see below, we now have our sqlserver container running with the same container ID that it displayed before, it shortened the ID tho, so we only see the first part.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--kHy00THT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://owendavies.net/assets/docker-sqlserver/docker-container-ls.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--kHy00THT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://owendavies.net/assets/docker-sqlserver/docker-container-ls.png" alt="docker container ls"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Connect to SQL Server from Management Studio
&lt;/h2&gt;

&lt;p&gt;Now lets connect to the container with SQL Management Studio, and create some tables.&lt;/p&gt;

&lt;p&gt;Fire up Management Studio, and connect to localhost, with username sa and the password we set in the docker run command &lt;em&gt;Sample123$&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You should now be connected to the database running in the container, you can go ahead and add a database and a table, and insert some data. But when we remove the container, and then run the container again our data will be gone.&lt;/p&gt;

&lt;h2&gt;
  
  
  Remove the container
&lt;/h2&gt;

&lt;p&gt;To remove the container we first must stop the container, and then we can use the &lt;em&gt;docker container rm container_name&lt;/em&gt; command to remove it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker container stop /sqlserver 
docker container rm /sqlserver 

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Check the container has gone
&lt;/h2&gt;

&lt;p&gt;Now when we run &lt;em&gt;docker container ls&lt;/em&gt; we see the container is gone&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker container ls

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

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--kHy00THT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://owendavies.net/assets/docker-sqlserver/docker-container-ls.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--kHy00THT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://owendavies.net/assets/docker-sqlserver/docker-container-ls.png" alt="docker container ls"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Run the container again and check the data is no longer there
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Now we can run the exact same &lt;em&gt;docker run&lt;/em&gt; command as before:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker run -e "ACCEPT_EULA=Y" -e "SA_PASSWORD=Sample123$" -p 1433:1433 --name sqlserver -d mcr.microsoft.com/mssql/server:latest

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

&lt;/div&gt;



&lt;p&gt;Check that it is running with &lt;em&gt;docker container ls&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Connect to SQL Server from Management Studio again as before
&lt;/h3&gt;

&lt;p&gt;Connect to the database with Management studio and we can see that the database has gone. To persist this data we need to setup a volume mount point, and point our container there.&lt;/p&gt;

&lt;h1&gt;
  
  
  3. Create volume mount point for persistent data
&lt;/h1&gt;

&lt;p&gt;We can create a volume mount point which will make a location on the local disk the stored location for the database files inside the container, this way each time we remove the container we will be able to retain&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker run -e "ACCEPT_EULA=Y" -e "SA_PASSWORD=Password123" -p 1433:1433 
--name sqlserver -v C:\Users\Owen\DockerVolumes\sqlserver:/var/opt/mssql/data 
-d mcr.microsoft.com/mssql/server:latest

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

&lt;/div&gt;



&lt;h1&gt;
  
  
  4. Set this up with a docker-compose file
&lt;/h1&gt;

&lt;p&gt;To save having to to remember/type all of these parameters each time we want to use our SQL database, we can do the exact same thing with a docker-compose.yaml file. This will mean we can just run a single simple command and all the parameters will be contained in the yaml file.&lt;/p&gt;

&lt;p&gt;Realistically this is much easier, as generally I have so many containers I don’t want to have to remember everything about each of them.&lt;/p&gt;

&lt;p&gt;Take the below code, save it as docker-compose.yml in your project root.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;version: '3.4'

services:
  sqlserver:
    image: mcr.microsoft.com/mssql/server
    container_name: sqlserver
    environment:
      - ACCEPT_EULA=Y
      - SA_PASSWORD=Password123
    ports:
      - "1433:1433"
    volumes:
      - C:\Users\Owen\DockerVolumes\sqlserver:/var/opt/mssql/data 

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

&lt;/div&gt;



&lt;p&gt;Now instead of having to type the long command from before all we have to type is the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker-compose up

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Connect to the database with Management Studio
&lt;/h3&gt;

&lt;p&gt;Now you can connect to the database with Management Studio, add data and use as normal. You can then stop and remove the container and retain all the data in the volume you specified. This data can be test data that you store somewhere central so that your whole team can share it and of course version it, or somewhere local to you.&lt;/p&gt;

</description>
      <category>docker</category>
      <category>sqlserver</category>
    </item>
    <item>
      <title>Getting started with Terraform</title>
      <dc:creator>Owen Davies</dc:creator>
      <pubDate>Thu, 10 Oct 2019 00:00:00 +0000</pubDate>
      <link>https://dev.to/owendavies/getting-started-with-terraform-4k14</link>
      <guid>https://dev.to/owendavies/getting-started-with-terraform-4k14</guid>
      <description>&lt;p&gt;Terraform allows you to setup Infrastructure as expressed by code, (Infrastructure As Code IaC). It’s platform independent, and supports a wide range of different things, from cloud providers like Azure, AWS, GCP, to databases to DNS, pretty much everything.&lt;/p&gt;

&lt;h1&gt;
  
  
  What we're going to do:
&lt;/h1&gt;

&lt;ol&gt;
&lt;li&gt;Install Terraform on Debian&lt;/li&gt;
&lt;li&gt;Install Docker&lt;/li&gt;
&lt;li&gt;Create a terraform script to download a docker image of jekyll&lt;/li&gt;
&lt;li&gt;Modify our terraform script to spin up the jekyll container.&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;
  
  
  Setting up your workstation
&lt;/h1&gt;

&lt;p&gt;I’m using Debian 10 (buster) for my installation here but Terraform can literally be installed on anything since it is written in golang.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install Terraform
&lt;/h2&gt;

&lt;p&gt;Download the most recent package for your operating system from &lt;a href="https://www.terraform.io/downloads.html"&gt;the terraform download page&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;curl -O https://releases.hashicorp.com/terraform/0.12.10/terraform_0.12.10_linux_amd64.zip

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Unzip the package into our /usr/local/bin to install it locally
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo unzip terraform_0.11.10_linux_amd64.zip -d /usr/local/bin

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Confirm it’s working
&lt;/h2&gt;

&lt;p&gt;Now lets run the following command to check that it’s working and available&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;terraform -version

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

&lt;/div&gt;



&lt;p&gt;You should receive the following version output &lt;em&gt;Terraform v0.12.10&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Jld-UP3U--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/v1/terraform-getting-started/terraform-version.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Jld-UP3U--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/v1/terraform-getting-started/terraform-version.png" alt="Terraform version"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see Terraform is still not at version 1, it’s still in beta and is in active development and each version can and has change quite drastically. So just be aware of this.&lt;/p&gt;

&lt;h1&gt;
  
  
  Install Docker
&lt;/h1&gt;

&lt;p&gt;If you already have docker installed locally you can skip these steps&lt;/p&gt;

&lt;p&gt;Install the prerequisites to add a new repository over https&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-get update &amp;amp;&amp;amp; apt-get install apt-transport-https ca-certificates curl software-properties-common

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

&lt;/div&gt;



&lt;p&gt;Import the GPG key from docker&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl -fsSL https://download.docker.com/linux/debian/gpg | sudo apt-key add -

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

&lt;/div&gt;



&lt;p&gt;Add the docker apt repository to our repository list&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/debian $(lsb_release -cs) stable"

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

&lt;/div&gt;



&lt;p&gt;update apt and install Docker CE&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-get update
sudo apt-get install docker-ce

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

&lt;/div&gt;



&lt;h1&gt;
  
  
  Creating our first Terraform script
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Pull a docker image from the Terraform Docker provider
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Create our new script
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir terraform-gettingstarted
touch main.tf

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Create the script content
&lt;/h3&gt;

&lt;p&gt;Now open up the main.tf in your favourite text editor and add the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Download the latest jekyll image
resource "docker_image" "image_id" {
  name = "jekyll:latest"
}                                 

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Initialise our Terraform directory
&lt;/h3&gt;

&lt;p&gt;This command will initialise our directory by creating a .terraform directory. And download any providers that are necessary.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;terraform init

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

&lt;/div&gt;



&lt;p&gt;This will download the terraform docker provider. The providers are stored in the .terraform directory. If you look in .terraform/plugins/linux_amd64/ you will see the terraform provider.&lt;/p&gt;

&lt;h3&gt;
  
  
  View the changes that will happen
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;terraform plan

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

&lt;/div&gt;



&lt;p&gt;By running &lt;em&gt;terraform plan&lt;/em&gt; we will see all the changes that are going to be applied.&lt;/p&gt;

&lt;h3&gt;
  
  
  Run our new script
&lt;/h3&gt;

&lt;p&gt;Once we have viewed the plan, and we are happy it isn’t doing something we didn’t expect we will 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;terraform apply

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

&lt;/div&gt;



&lt;p&gt;This will download the docker image that we specified in the main.tf file.&lt;/p&gt;

&lt;h3&gt;
  
  
  See what was applied
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;terraform show

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

&lt;/div&gt;



&lt;p&gt;This will return the docker image id that we asked it to download&lt;/p&gt;

&lt;h1&gt;
  
  
  Interpolation syntax
&lt;/h1&gt;

&lt;p&gt;Interpolational syntax is a way for you to reference variables and other objects from within your infrastructure.&lt;/p&gt;

&lt;p&gt;The documentation on terraform.io explains this in depth. (Link)[&lt;a href="https://www.terraform.io/docs/configuration/interpolation.html"&gt;https://www.terraform.io/docs/configuration/interpolation.html&lt;/a&gt;]&lt;/p&gt;

&lt;h2&gt;
  
  
  Create a docker container of the image
&lt;/h2&gt;

&lt;p&gt;Let’s add to our main.tf file and actually create a container using the image above, referenced with interpolation syntax&lt;/p&gt;

&lt;h3&gt;
  
  
  Open main.tf and edit
&lt;/h3&gt;

&lt;p&gt;Let’s go ahead and open the main.tf file that we created above, and add the following code below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Start the container
resource "docker_container" "container_id" {
  name = "blog"
  image = "${docker_image.image_id.latest}"
  ports {
    internal = "2368"
    external = "80"
  }
}

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

&lt;/div&gt;



&lt;p&gt;You can see the $ sign, this is how we are referencing the image above.&lt;/p&gt;

&lt;h3&gt;
  
  
  Run our changes
&lt;/h3&gt;

&lt;p&gt;Now lets view the plan of the changes by running the below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;terraform plan

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

&lt;/div&gt;



&lt;p&gt;You’ll see that it is referencing our id of our image&lt;/p&gt;

&lt;p&gt;Now lets apply our changes&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;terraform apply

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

&lt;/div&gt;



&lt;p&gt;Now if you run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker ps -a

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

&lt;/div&gt;



&lt;p&gt;You will see our container is running with the ports open.&lt;/p&gt;

&lt;p&gt;If you now browse to port 80 in a web browser you will see the jekyll blog setup.&lt;/p&gt;

&lt;h2&gt;
  
  
  Clear terraform infrastructure
&lt;/h2&gt;

&lt;p&gt;To remove all the infrastructure we just created above we can use the destroy command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;terraform destroy

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

&lt;/div&gt;



&lt;p&gt;It will tell you what you are about to destroy, and require you to type &lt;em&gt;yes&lt;/em&gt;.&lt;/p&gt;

</description>
      <category>terraform</category>
      <category>docker</category>
    </item>
    <item>
      <title>Replacing SSMTP with MSMTP in Docker</title>
      <dc:creator>Owen Davies</dc:creator>
      <pubDate>Mon, 26 Aug 2019 00:00:00 +0000</pubDate>
      <link>https://dev.to/owendavies/replacing-ssmtp-with-msmtp-in-docker-1kmm</link>
      <guid>https://dev.to/owendavies/replacing-ssmtp-with-msmtp-in-docker-1kmm</guid>
      <description>&lt;p&gt;Trying to build a new image of a Docker file that I’ve been running for months, and I’m greeted with this error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Package ssmtp is not available, but is referred to by another package.
This may mean that the package is missing, has been obsoleted, or
is only available from another source

E: Package 'ssmtp' has no installation candidate

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

&lt;/div&gt;



&lt;p&gt;Looks like &lt;a href="https://wiki.debian.org/sSMTP"&gt;sSMTP&lt;/a&gt; is no longer maintained, and has been orphaned since 19th March 2019. &lt;a href="https://wiki.debian.org/msmtp"&gt;MSMTP&lt;/a&gt; is the suggested replacement. So let’s get replacing.&lt;/p&gt;

&lt;h1&gt;
  
  
  The plan
&lt;/h1&gt;

&lt;ol&gt;
&lt;li&gt;Install mSMTP in our Docker image&lt;/li&gt;
&lt;li&gt;Configure mSMTP to our smtp server (in this case Office 365)&lt;/li&gt;
&lt;li&gt;Tell PHP to use mSMTP for email instead of sSMTP&lt;/li&gt;
&lt;li&gt;Send a test email&lt;/li&gt;
&lt;li&gt;Final source&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  1. Install mSMTP in our Docker image
&lt;/h2&gt;

&lt;p&gt;We’re going to start with the &lt;em&gt;php:7.30-apache&lt;/em&gt; docker image&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM php:7.3-apache

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

&lt;/div&gt;



&lt;p&gt;Lets install msmtp with apt-get, and tidy up afterwards:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;apt-get update &amp;amp;&amp;amp; apt-get install ssmtp -y &amp;amp;&amp;amp; \
rm -rf /var/lib/apt/lists/*

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  2. Configure mSMTP to our smtp server (in this case Office 365)
&lt;/h2&gt;

&lt;p&gt;We need to setup a configuration file for msmtp to have the smtp details. The msmtp config location is in /etc/&lt;/p&gt;

&lt;p&gt;Let’s create a new file called msmtprc, and add the following configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;account default
host smtp.office365.com
port 587
tls on
tls_starttls on
tls_trust_file /etc/ssl/certs/ca-certificates.crt
tls_certcheck on
auth on
user user@domain.com
password "YourAwesomeStr0ngP4zzw0rd"
from "user@domain.com"
logfile /var/log/msmtp.log

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

&lt;/div&gt;



&lt;p&gt;This is for Office 365, change the smtp details to gmail or your own smtp server.&lt;/p&gt;

&lt;h3&gt;
  
  
  Copy the config into our Docker image
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;COPY msmtprc /etc/msmtprc

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Make the msmtprc config file readable/writable by its owner
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;RUN chmod 600 /etc/msmtprc

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  3. Tell PHP to use mSMTP for email instead of sSMTP
&lt;/h2&gt;

&lt;p&gt;Now that we have mSMTP setup, we need to tell PHP to use it.&lt;/p&gt;

&lt;h3&gt;
  
  
  You can get a copy of the default php.ini file from the docker image we have just created. It’s located in &lt;em&gt;/usr/local/etc/php/php.ini-production&lt;/em&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker cp &amp;lt;CONTAINER_ID&amp;gt;:/usr/local/etc/php/php.ini.production .

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Open up your php.ini file, and make the following change to the sendmail_path variable:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sendmail_path = /usr/bin/msmtp -t

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Copy your php.ini file to the Docker image php config location:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;COPY php.ini $PHP_INI_DIR/php.ini

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  4. Send a test email
&lt;/h2&gt;

&lt;p&gt;With all that setup we should now be able to send an email.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Build the Docker image
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker build . -t php-msmtp-setup:latest

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Run the container
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker run -d -p 80:80/tcp php-msmtp-setup:latest

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Log into the container
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Run the following command to find the CONTAINER ID of our new container
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker container ls

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

&lt;/div&gt;



&lt;h4&gt;
  
  
  Run the following command to login to the container
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker exec -ti &amp;lt;CONTAINER_ID&amp;gt; /bin/bash

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Send an email directly with msmtp
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;echo -e "Subject: Test Mail\r\n\r\nThis is a test mail, let me know if this works" |msmtp --debug --from from@yourdomain.com -t to@someone.com

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  5. Send an email with php
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;php -r "mail('to@domain.com','Test Mail from PHP', 'This is a test mail from PHP, let me know if this works');"

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  5. Final source
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Our &lt;em&gt;Docker&lt;/em&gt; file
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM php:7.3-apache

RUN apt-get update &amp;amp;&amp;amp; apt-get install msmtp -y &amp;amp;&amp;amp; \
    rm -rf /var/lib/apt/lists/*

COPY msmtprc /etc/msmtprc
RUN chmod 600 /etc/msmtprc

COPY php.ini $PHP_INI_DIR/php.ini

EXPOSE 80

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  The changes to our &lt;em&gt;php.ini&lt;/em&gt; file
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sendmail_path = /usr/bin/msmtp -t

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Our &lt;em&gt;msmtprc&lt;/em&gt; file
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;account default
host smtp.office365.com
port 587
tls on
tls_starttls on
tls_trust_file /etc/ssl/certs/ca-certificates.crt
tls_certcheck on
auth on
user user@domain.com
password "YourAwesomeStr0ngP4zzw0rd"
from "user@domain.com"
logfile /var/log/msmtp.log

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

&lt;/div&gt;



</description>
      <category>docker</category>
      <category>php</category>
      <category>msmtp</category>
    </item>
    <item>
      <title>Get latest Azure API Version in ARM templates</title>
      <dc:creator>Owen Davies</dc:creator>
      <pubDate>Thu, 24 May 2018 00:00:00 +0000</pubDate>
      <link>https://dev.to/owendavies/get-latest-azure-api-version-in-arm-templates-3ibc</link>
      <guid>https://dev.to/owendavies/get-latest-azure-api-version-in-arm-templates-3ibc</guid>
      <description>&lt;p&gt;The following script will crawl through all your ARM templates in a folder and check for the latest Azure API version.&lt;/p&gt;

&lt;p&gt;This is useful if you want to make sure you are staying up to date with the latest API.&lt;/p&gt;

&lt;p&gt;Currently there is no way to be notified if your ARM templates have a newer version of the API. And if you are anything like me you have hundreds of nested ARM templates to keep up to date. Keeping track of all the resources you are using in all your files is difficult.&lt;/p&gt;

&lt;p&gt;This will take into account if you hold your resource API versions inside variables, parameters or directly in the apiVersion itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Examples of where it looks
&lt;/h2&gt;

&lt;p&gt;I store Resource API versions in multiple places depending on a few different reasons in my ARM templates. I try to store in Variables as much as possible for reuse, but often I need to store in parameters so that I can pass in to a nested template.&lt;/p&gt;

&lt;p&gt;The following JSON ARM Template file has examples of storing the API version in 3 places:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Directly in the resource&lt;/li&gt;
&lt;li&gt;In Variables&lt;/li&gt;
&lt;li&gt;In Parameters
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "apiVersionDisks": "2017-06-01"
  },
  "variables": {
    "apiVersionResourcesDeployment": "2017-05-10",
    "apiVersionDisks": "2017-04-01",
    "apiVersionStorage": "2017-04-01"
  },
  "resources": [{
      "apiVersion": "[variables('apiVersionResourcesDeployment')]",
      "type": "Microsoft.Resources/deployments"
    },
    {
      "name": "Name1",
      "type": "Microsoft.Storage/storageAccounts",
      "apiVersion": "[variables('apiVersionStorage')]",
    },
    {
      "name": "Name1",
      "type": "Microsoft.Compute/disks",
      "apiVersion": "[parameters('apiVersionDisks')]",
    },
    {
      "name": "Name1",
      "type": "Microsoft.Compute/disks",
      "apiVersion": "2015-06-15",
    }
  ]
}

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



&lt;h2&gt;
  
  
  The Script!
&lt;/h2&gt;

&lt;p&gt;This script will check all 3 locations for API versions to check against.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$templatePath = 'C:\YOUR\ARM\PATH'

$listOfResources = @()

Get-ChildItem $templatePath -Recurse -Filter *.json |
Foreach-Object {

    $content = (Get-Content $_.FullName | ConvertFrom-Json)

    foreach($resource in $content.resources)
    {
        $resourceObject = New-Object -TypeName pscustomobject -Property @{
            "fileName" = $_.FullName
            "resourceName" = $($resource.type)
            "apiVersion" = ""
            "apiVersionVariableName" = ""
            "apiVersionParameterName" = ""
            "latestApiVersion" = ""
        }

        if($($resource.apiVersion) -like '*variables*')
        {
            $apiVariable = ($resource.apiVersion).replace("[variables('","").replace("')]","")

            $resourceObject.apiVersionVariableName = $apiVariable
            $resourceObject.apiVersion = $($($content.variables).$apiVariable)
        }
        elseif($resource.apiVersion -like '*parameters*')
        {
            $apiVariable = ($resource.apiVersion).replace("[parameters('","").replace("')]","")

            $apiParameter = ($resource.apiVersion).replace("[parameters('","").replace("')]","")

            $resourceObject.apiVersionParameterName = $apiParameter
            $resourceObject.apiVersion = $($($content.parameters).$apiParameter)
        }
        else
        {
            $resourceObject.apiVersion = $($resource.apiVersion)
        }

        $listOfResources += $resourceObject
    }
}

$resources = $listOfResources.resourceName | Sort-Object | Get-Unique

Foreach($i in $resources)
{   
    $providerNamespace,$resourceTypeName = $i.split("/",2)
    $latestApiVersion = ((Get-AzureRmResourceProvider -ProviderNamespace $providerNamespace).ResourceTypes | Where-Object ResourceTypeName -eq $resourceTypeName).ApiVersions | select -First 1

    $editObj = $listOfResources | where {$_.resourceName -eq $i}
    foreach($r in $editObj)
    {
        $r.latestApiVersion=$latestApiVersion   
    }

}

Foreach($resource in $listOfResources)
{
    if($resource.latestApiVersion -ne $resources.apiVersion)
    {
        Write-Host "`nThere is a new ApiVersion for $($resource.resourceName)"
        Write-Host "Filename: [$($resource.fileName)]"
        Write-Host "Current ApiVersion: [$($resource.apiVersion)]"
        Write-Host "Latest ApiVersion: [$($resource.latestApiVersion)]"
    }
}

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



&lt;p&gt;Please let me know if you’ve found this useful! :-)&lt;/p&gt;

</description>
      <category>azure</category>
      <category>arm</category>
      <category>microsoftazure</category>
    </item>
  </channel>
</rss>
