<?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: Anton Sizikov</title>
    <description>The latest articles on DEV Community by Anton Sizikov (@asizikov).</description>
    <link>https://dev.to/asizikov</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%2F4133%2F819053.png</url>
      <title>DEV Community: Anton Sizikov</title>
      <link>https://dev.to/asizikov</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/asizikov"/>
    <language>en</language>
    <item>
      <title>Show off your DevOps efforts on GitHub</title>
      <dc:creator>Anton Sizikov</dc:creator>
      <pubDate>Tue, 16 Mar 2021 20:49:22 +0000</pubDate>
      <link>https://dev.to/asizikov/show-off-your-devops-efforts-on-github-34p7</link>
      <guid>https://dev.to/asizikov/show-off-your-devops-efforts-on-github-34p7</guid>
      <description>&lt;p&gt;In the era of DevOps some of us write yaml-files on a daily basis. Sometimes it's just a lot of yamls. I would say that in some repositories YAML is more than just 'a data-file', it's a valuable artifact where the core (or just important) logic resides. &lt;/p&gt;

&lt;p&gt;However GitHub doesn't think so. You know that there is a Language Breakdown Graph on every GitHub repository: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1kaGViwP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0fi47inpuioi37tlbocf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1kaGViwP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0fi47inpuioi37tlbocf.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It looks cool, it highlights the languages used in this repo. &lt;br&gt;
However, your DevOps efforts are not represented on this chart. No Kubernetes or Docker-Compose manifests and Charts, no CI/CD pipelines, no GitHub Actions, and other sometimes tricky configurations. &lt;/p&gt;

&lt;p&gt;Luckily this is something we can fix. &lt;/p&gt;
&lt;h3&gt;
  
  
  GitHub Linguist
&lt;/h3&gt;

&lt;p&gt;GitHub uses a library called &lt;code&gt;Linguist&lt;/code&gt; (&lt;a href="https://github.com/github/linguist"&gt;https://github.com/github/linguist&lt;/a&gt;) to analyze your repository. Of course, the Language Breakdown Graph is more of a side-effect and definitely not the main purpose of this analysis. But for now we'll focus on this particular feature of &lt;code&gt;Linguist&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;All languages known to Linguist can be found in the &lt;code&gt;languages.yml&lt;/code&gt; file in the linguist repository. &lt;/p&gt;

&lt;p&gt;Let's &lt;a href="https://github.com/github/linguist/blob/072bc3da45b58765db41bd93818121a6ae86aaf2/lib/linguist/languages.yml#L6443"&gt;check &lt;code&gt;YAML&lt;/code&gt; definition&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;YAML&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;data&lt;/span&gt;
  &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;#cb171e"&lt;/span&gt;
  &lt;span class="na"&gt;tm_scope&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;source.yaml&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;as you can see, it's treated as a data file by default and thus it's left out from the graph.&lt;/p&gt;

&lt;h3&gt;
  
  
  Overriding Linguist behavior
&lt;/h3&gt;

&lt;p&gt;We can add (or edit existing) &lt;code&gt;.gitattributes&lt;/code&gt; file with the following content to the root of our repository:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;*&lt;/span&gt;.yml linguist-detectable&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;
&lt;span class="k"&gt;*&lt;/span&gt;.yml linguist-language&lt;span class="o"&gt;=&lt;/span&gt;YAML
&lt;span class="k"&gt;*&lt;/span&gt;.yaml linguist-detectable&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;
&lt;span class="k"&gt;*&lt;/span&gt;.yaml linguist-language&lt;span class="o"&gt;=&lt;/span&gt;YAML
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and Linguist will respect that override, by showing us a proper Language Breakdown on your page:&lt;/p&gt;

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

&lt;p&gt;Isn't it cool? &lt;/p&gt;

&lt;h3&gt;
  
  
  There is more
&lt;/h3&gt;

&lt;p&gt;It's not just the way YAML files are treated. You can fine-tune that to emphasize the work which is important in &lt;em&gt;your&lt;/em&gt; opinion. &lt;/p&gt;

&lt;p&gt;Do you have a repository full of Markdown files, perhaps a book that you're writing? Well, &lt;a href="https://github.com/github/linguist/blob/master/lib/linguist/languages.yml#L3252-L3254"&gt;Markdown&lt;/a&gt; can be marked as &lt;code&gt;linguist-detectable&lt;/code&gt; as well.&lt;/p&gt;

&lt;p&gt;Do you have some samples or generated files that you want to exclude? &lt;code&gt;linguist-generated&lt;/code&gt; and &lt;code&gt;linguist-documentation&lt;/code&gt; attributes can be used. &lt;/p&gt;

&lt;p&gt;You can go wild and mess up with it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Example of a `.gitattributes` file &lt;/span&gt;
&lt;span class="c"&gt;# which reclassifies `.rb` files as Java:&lt;/span&gt;
&lt;span class="k"&gt;*&lt;/span&gt;.rb linguist-language&lt;span class="o"&gt;=&lt;/span&gt;Java
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Additional documentation and examples can be found &lt;a href="https://github.com/github/linguist/blob/master/docs/overrides.md#summary"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Call to Action
&lt;/h3&gt;

&lt;p&gt;I hope you enjoyed this little trick. Now go and celebrate your DevOps work by including it in your repository stats. &lt;/p&gt;

</description>
      <category>devops</category>
      <category>github</category>
      <category>git</category>
      <category>tips</category>
    </item>
    <item>
      <title>Configure CD for Azure WebApp with Terraform Provider for Octopus Deploy</title>
      <dc:creator>Anton Sizikov</dc:creator>
      <pubDate>Thu, 28 Jan 2021 20:29:41 +0000</pubDate>
      <link>https://dev.to/asizikov/configure-cd-for-azure-webapp-with-terraform-provider-for-octopus-deploy-4iac</link>
      <guid>https://dev.to/asizikov/configure-cd-for-azure-webapp-with-terraform-provider-for-octopus-deploy-4iac</guid>
      <description>&lt;p&gt;In this post, I'm going to configure the continuous delivery process for Azure WebApp (Azure Function in this case, but that's pretty much the same) with Octopus Deploy. To make it a little bit interesting I'm going to use Configuration-as-Code approach with a brand new &lt;a href="https://registry.terraform.io/providers/OctopusDeployLabs/octopusdeploy/latest" rel="noopener noreferrer"&gt;Octopus provider for Terraform&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Buckle up and let's get started...&lt;/p&gt;

&lt;h2&gt;
  
  
  Tools
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Terraform
&lt;/h3&gt;

&lt;p&gt;I'm going to need &lt;code&gt;terraform&lt;/code&gt; on my machine:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.cloud-eng.nl%2Fimages%2F2021-01-tf%2Ftf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.cloud-eng.nl%2Fimages%2F2021-01-tf%2Ftf.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Octopus Deploy
&lt;/h3&gt;

&lt;p&gt;For this post, I've signed up for a &lt;a href="https://octopus.com/start/cloud" rel="noopener noreferrer"&gt;free tier of Octopus Deploy SaaS offering&lt;/a&gt;. Of course, the self-hosted version will work as well.&lt;/p&gt;

&lt;p&gt;When signed up and configured my account I can generate a new API key that I'm going to use with Terraform&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.cloud-eng.nl%2Fimages%2F2021-01-tf%2Fapi-key.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.cloud-eng.nl%2Fimages%2F2021-01-tf%2Fapi-key.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;All configuration is going to be made against the default Space (&lt;code&gt;Spaces-1&lt;/code&gt;), however, that's configurable too.&lt;/p&gt;

&lt;h3&gt;
  
  
  Azure
&lt;/h3&gt;

&lt;p&gt;To connect Octopus Deploy with Azure I'm going to need a Service Principal.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# (!) make sure to follow the principle of least privilege here &lt;/span&gt;
&lt;span class="c"&gt;# and define the role and scope&lt;/span&gt;
az ad sp create-for-rbac &lt;span class="nt"&gt;--name&lt;/span&gt; sp-octopus-deploy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Grap the tenant Id, application Id, password, and subscription Id values. We'll heed them later.&lt;/p&gt;
&lt;h3&gt;
  
  
  Azure Function
&lt;/h3&gt;

&lt;p&gt;For the demo, I'm going to use this super simple Azure Function that replies to your GET request with a list of headers supplied.&lt;/p&gt;

&lt;p&gt;The complete source code is &lt;a href="https://github.com/asizikov/terraform-octopus-deploy/tree/main/src/echo_api" rel="noopener noreferrer"&gt;published here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;the &lt;code&gt;.csx&lt;/code&gt; gist is the following&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



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

&lt;p&gt;I'm running the CI part in Azure DevOps.&lt;/p&gt;

&lt;p&gt;Project configuration is pretty simple and it's terraform set up &lt;a href="https://github.com/asizikov/terraform-octopus-deploy/tree/main/infra/azure_devops" rel="noopener noreferrer"&gt;can be found here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;the pipeline itself it &lt;a href="https://github.com/asizikov/terraform-octopus-deploy/blob/main/build/pipeline.yaml" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Nothing complicated: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Build Azure Function&lt;/li&gt;
&lt;li&gt;Pack Azure DevOps artifact (zip)&lt;/li&gt;
&lt;li&gt;Upload it to Octopus Deploy's built-in feed&lt;/li&gt;
&lt;li&gt;Create and trigger Octopus Deploy release &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'm still waiting for &lt;a href="https://help.octopus.com/t/cannot-set-up-an-integration-with-azure-devops/26216" rel="noopener noreferrer"&gt;the fix to my bug-report to be delivered&lt;/a&gt;, without it I cannot make this demo end-to-end :(&lt;/p&gt;

&lt;h2&gt;
  
  
  Init
&lt;/h2&gt;

&lt;p&gt;Terraform provider is no different from other providers and can be found on &lt;a href="https://registry.terraform.io/providers/OctopusDeployLabs/octopusdeploy/latest" rel="noopener noreferrer"&gt;Terraform Registry&lt;/a&gt;&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;and &lt;code&gt;terraform init&lt;/code&gt; will download missing files&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.cloud-eng.nl%2Fimages%2F2021-01-tf%2Finit.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.cloud-eng.nl%2Fimages%2F2021-01-tf%2Finit.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Configuration
&lt;/h2&gt;

&lt;p&gt;For the demo, I'm going to have this &lt;code&gt;terraform.tfvars&lt;/code&gt; file to provide configuration values to Terraform.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


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

&lt;p&gt;Now we're ready to start. At first, I'm going to configure a new &lt;a href="https://octopus.com/docs/infrastructure/environments" rel="noopener noreferrer"&gt;Environment&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Environments are how you organize your deployment targets (whether on-premises servers or cloud services) into groups that represent the different stages of your deployment pipeline, for instance, development, test, and production.&lt;/p&gt;
&lt;/blockquote&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;You'd probably have more than one, but that'll do for now.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deployment Target
&lt;/h2&gt;

&lt;p&gt;The next crucial part is &lt;a href="https://octopus.com/docs/infrastructure/deployment-targets" rel="noopener noreferrer"&gt;Deployment Target&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;With Octopus Deploy, you can deploy software to Windows servers, Linux servers, Microsoft Azure, AWS, Kubernetes clusters, cloud regions, or an offline package drop. Regardless of where you're deploying your software, these machines and services are known as your deployment targets.&lt;/p&gt;
&lt;/blockquote&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;I'm using &lt;code&gt;octopusdeploy_azure_web_app_deployment_target&lt;/code&gt;, It's quite specific. I could have used a more generic &lt;a href="https://registry.terraform.io/providers/OctopusDeployLabs/octopusdeploy/latest/docs/resources/deployment_target" rel="noopener noreferrer"&gt;&lt;code&gt;octopusdeploy_deployment_target&lt;/code&gt;&lt;/a&gt; instead.&lt;/p&gt;

&lt;p&gt;This is where I link Azure (my Service Principal) with Octopus.&lt;/p&gt;

&lt;p&gt;Now we have a nice and clean infrastructure defined in Octopus.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.cloud-eng.nl%2Fimages%2F2021-01-tf%2Fenvironment.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.cloud-eng.nl%2Fimages%2F2021-01-tf%2Fenvironment.png"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;The next step is to configure our &lt;a href="https://octopus.com/docs/projects" rel="noopener noreferrer"&gt;Project&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Projects let you create and manage your deployment processes, releases, and runbooks from the Octopus REST API and Octopus Web Portal.&lt;/p&gt;
&lt;/blockquote&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;I'm setting up an &lt;code&gt;Echo Api&lt;/code&gt; project here, placing it into &lt;code&gt;Default Project Group&lt;/code&gt; with the default &lt;a href="https://octopus.com/docs/releases/lifecycles" rel="noopener noreferrer"&gt;lifecycle&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.cloud-eng.nl%2F%2Fimages%2F2021-01-tf%2Fproject.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.cloud-eng.nl%2F%2Fimages%2F2021-01-tf%2Fproject.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Deployment process
&lt;/h2&gt;

&lt;p&gt;And now the juicy part. &lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;My deployment is super simple actually, just one step that is picking up the package from a built-in feed and pushing it to my only environment. &lt;/p&gt;

&lt;p&gt;I wish all deployments were that simple...&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.cloud-eng.nl%2Fimages%2F2021-01-tf%2Fprocess.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.cloud-eng.nl%2Fimages%2F2021-01-tf%2Fprocess.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Release
&lt;/h2&gt;

&lt;p&gt;Initially, I wanted to automate the release creation and trigger it via my Azure DevOps pipeline, but due to a very recent bug, I'm blocked here. I'll update this post when things are fixed so for now just a manual process.&lt;/p&gt;

&lt;p&gt;I'm creating a new release from the zip file in my built-in package feed&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.cloud-eng.nl%2Fimages%2F2021-01-tf%2Frelease.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.cloud-eng.nl%2Fimages%2F2021-01-tf%2Frelease.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And now I can sit back and watch it deploying my function.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.cloud-eng.nl%2Fimages%2F2021-01-tf%2Fdeployment.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.cloud-eng.nl%2Fimages%2F2021-01-tf%2Fdeployment.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Voilà, it's up and running.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.cloud-eng.nl%2Fimages%2F2021-01-tf%2Frunning.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.cloud-eng.nl%2Fimages%2F2021-01-tf%2Frunning.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Import
&lt;/h2&gt;

&lt;p&gt;The neat part about Octopus Deploy is its resource ID's system. It's always easy to find the id of the object which makes it super easy to import existing resources into your terraform state.&lt;/p&gt;

&lt;p&gt;Let's assume that I already have an environment configured.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://cloud-eng.octopus.app/app#/Spaces-1/infrastructure/machines?environmentIds=Environments-7
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The URL looks this way. &lt;code&gt;Environments-7&lt;/code&gt; is the id of my environment. &lt;/p&gt;

&lt;p&gt;All it takes to import the environment is to declare the resource and type one command:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;this is such a breath comparing to long and not so easy to get Ids in Azure :)&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;this is a cross-post from my &lt;a href="https://blog.cloud-eng.nl/2021/01/27/terraform-octopus-deploy/" rel="noopener noreferrer"&gt;personal blog&lt;/a&gt;. &lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>devops</category>
      <category>octopusdeploy</category>
      <category>terraform</category>
      <category>azure</category>
    </item>
    <item>
      <title>Using GitHub Container Registry with Kubernetes</title>
      <dc:creator>Anton Sizikov</dc:creator>
      <pubDate>Thu, 08 Oct 2020 12:15:10 +0000</pubDate>
      <link>https://dev.to/asizikov/using-github-container-registry-with-kubernetes-38fb</link>
      <guid>https://dev.to/asizikov/using-github-container-registry-with-kubernetes-38fb</guid>
      <description>&lt;p&gt;GitHub Container Registry was introduced on the 1st of September 2020. It's still in the Beta stage, so it's rather not recommended to use it in production. However, it offers us free private storage for our Docker images, at least until the end of the Beta period. &lt;/p&gt;

&lt;p&gt;Private storage, free and unlimited download... looks like a good enough option for local development. &lt;/p&gt;

&lt;p&gt;In this post, I'm going to configure my local Kubernetes cluster to pull images from my GitHub Container Registry.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enable GitHub Container Registry
&lt;/h2&gt;

&lt;p&gt;First things first. We need to enable GitHub Container Registry for our account (Process for your organization is almost the same).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note GitHub Container Registry is in public beta state at the moment of writing, so it's subject to change. I recommend to follow &lt;a href="https://docs.github.com/en/free-pro-team@latest/packages/getting-started-with-github-container-registry" rel="noopener noreferrer"&gt;the official documentation&lt;/a&gt;, it's more likely to be up to date.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fasizikov.github.io%2Fimages%2F2020-10-ghk8s%2Fregistry.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fasizikov.github.io%2Fimages%2F2020-10-ghk8s%2Fregistry.jpg" width="800" height="527"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Create Access Tokens
&lt;/h2&gt;

&lt;p&gt;For now, Container Registry supports just one authorization option, which is Personal Access Token (PAT). I'm going to &lt;a href="https://github.com/settings/tokens" rel="noopener noreferrer"&gt;create two tokens here&lt;/a&gt; (note the scopes).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fasizikov.github.io%2Fimages%2F2020-10-ghk8s%2Fpat-tokens.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fasizikov.github.io%2Fimages%2F2020-10-ghk8s%2Fpat-tokens.png" width="800" height="122"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One token with &lt;code&gt;read:packages&lt;/code&gt; scope will be used for my local Kubernetes cluster to perform pull actions. And another token I'll use to manage images in my registry (remember Authentication step from GitHub Packages configuration page?).&lt;/p&gt;

&lt;h2&gt;
  
  
  .dockerconfigjson
&lt;/h2&gt;

&lt;p&gt;Once I have my PAT token created I can build an auth string using the following format:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;username:123123adsfasdf123123&lt;/code&gt; where &lt;code&gt;username&lt;/code&gt; is my GitHub username and &lt;code&gt;123123adsfasdf123123&lt;/code&gt; is the token with &lt;code&gt;read:packages&lt;/code&gt; scope.&lt;/p&gt;

&lt;p&gt;let's Base64 encode it first:&lt;/p&gt;

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

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"username:123123adsfasdf123123"&lt;/span&gt; | &lt;span class="nb"&gt;base64
&lt;/span&gt;&lt;span class="nv"&gt;dXNlcm5hbWU6MTIzMTIzYWRzZmFzZGYxMjMxMjM&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;


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

&lt;/div&gt;
&lt;p&gt;With this string I can compose a new &lt;code&gt;.dockerconfigjson&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"auths"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"ghcr.io"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"auth"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"dXNlcm5hbWU6MTIzMTIzYWRzZmFzZGYxMjMxMjM="&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;


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

&lt;/div&gt;
&lt;p&gt;Which we can  Base64 encode again:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt;  &lt;span class="s1"&gt;'{"auths":{"ghcr.io":{"auth":"dXNlcm5hbWU6MTIzMTIzYWRzZmFzZGYxMjMxMjM="}}}'&lt;/span&gt; | &lt;span class="nb"&gt;base64
&lt;/span&gt;&lt;span class="nv"&gt;eyJhdXRocyI6eyJnaGNyLmlvIjp7ImF1dGgiOiJkWE5sY201aGJXVTZNVEl6TVRJellXUnpabUZ6WkdZeE1qTXhNak09In19fQ&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;


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

&lt;/div&gt;
&lt;p&gt;and store it at as &lt;code&gt;dockerconfigjson.yaml&lt;/code&gt; file.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Note: base64 is not an encryption, so you should not commit this file! This is just for a demo.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And now we have prepared all the pieces, time to create a new secret&lt;/p&gt;

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

kubectl create &lt;span class="nt"&gt;-f&lt;/span&gt; dockerconfigjson.yaml
secret/dockerconfigjson-github-com created


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

&lt;/div&gt;
&lt;p&gt;Which can be used later on when we schedule our pods:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;And the final step is to check Pod Events (I'm using &lt;a href="https://k8slens.dev/" rel="noopener noreferrer"&gt;Lens Kubernetes IDE here&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fasizikov.github.io%2Fimages%2F2020-10-ghk8s%2Fimage-pulled.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fasizikov.github.io%2Fimages%2F2020-10-ghk8s%2Fimage-pulled.png" width="800" height="608"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Have fun, and enjoy it while it's free!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This is a cross post &lt;a href="https://asizikov.github.io/2020/10/08/using-github-registry-with-k8s/" rel="noopener noreferrer"&gt;from my personal blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>github</category>
      <category>docker</category>
    </item>
    <item>
      <title>Fixing console logs for Azure Functions running in a Docker container</title>
      <dc:creator>Anton Sizikov</dc:creator>
      <pubDate>Sat, 11 Jul 2020 15:54:06 +0000</pubDate>
      <link>https://dev.to/asizikov/fixing-console-logs-for-azure-functions-running-in-a-docker-container-1lod</link>
      <guid>https://dev.to/asizikov/fixing-console-logs-for-azure-functions-running-in-a-docker-container-1lod</guid>
      <description>&lt;p&gt;Local development of C# Azure Functions on macOS is still a bit painful. &lt;br&gt;
Even the simple-ish logging might cause issues. Let's assume that we have Azure Functions Core Tools installed and we have a basic function app with one &lt;code&gt;TimerTrigger&lt;/code&gt; function created.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--yep-K8f7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://asizikov.github.io/images/2020-07-func-logs/initial-state.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--yep-K8f7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://asizikov.github.io/images/2020-07-func-logs/initial-state.png" alt="Azure Functions app in Rider"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With the default Run/Debug configuration&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Djc4NABD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://asizikov.github.io/images/2020-07-func-logs/default-config.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Djc4NABD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://asizikov.github.io/images/2020-07-func-logs/default-config.png" alt="Run configuration"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can compile and run our function&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--WVm_ipzp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://asizikov.github.io/images/2020-07-func-logs/log-console.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WVm_ipzp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://asizikov.github.io/images/2020-07-func-logs/log-console.png" alt="Azure Functions Log"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Our function will start and log to console as expected.&lt;/p&gt;

&lt;p&gt;I wish all the functions were that simple, right? Unfortunately, sometimes we need more, sometimes we even have to use some shared libraries. So let's create one: &lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Nothing fancy there, one class, one interface accepting the &lt;code&gt;ILogger&amp;lt;T&amp;gt;&lt;/code&gt; as a dependency.&lt;/p&gt;

&lt;p&gt;Let's update function code accordingly: will make is non-static, and use construction injection to get the &lt;code&gt;IAsyncGreeter&lt;/code&gt; injected:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;In order to register this external dependency and use a propper .NET Core-style DI we need to install &lt;code&gt;Microsoft.Azure.Functions.Extensions&lt;/code&gt; NuGet package and create a &lt;code&gt;Startup&lt;/code&gt; class like this one: &lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;You would expect it to work, right?&lt;/p&gt;

&lt;p&gt;I wish...&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--awX_DICk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://asizikov.github.io/images/2020-07-func-logs/func-logs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--awX_DICk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://asizikov.github.io/images/2020-07-func-logs/func-logs.png" alt="Just the main app is logging"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see it still just logs from the function.&lt;/p&gt;

&lt;p&gt;Why would one logger print it's output to console while another one just flushes our logs to void?&lt;/p&gt;

&lt;p&gt;Because they are two different loggers with different settings. One logger is instantiated and configured in our &lt;code&gt;Startup.cs&lt;/code&gt; class and another one is built and configured by function host and injected into the method instead. Oh, well.&lt;/p&gt;

&lt;p&gt;I want to run this function in docker, so let's create a &lt;code&gt;Dockerfile&lt;/code&gt;.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Note that I'm setting &lt;code&gt;AzureFunctionsJobHost__Logging__Console__IsEnabled&lt;/code&gt; environment variable here.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Goaou9AH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://asizikov.github.io/images/2020-07-func-logs/docker-log.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Goaou9AH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://asizikov.github.io/images/2020-07-func-logs/docker-log.png" alt="Just the main app is logging"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The missing link here is the &lt;code&gt;hosts.json&lt;/code&gt; logging configuration.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nl"&gt;"logLevel"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"default"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Information"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;And voilà, we've got logs in our container: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--IwxiDj3L--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://asizikov.github.io/images/2020-07-func-logs/docker-fixed.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--IwxiDj3L--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://asizikov.github.io/images/2020-07-func-logs/docker-fixed.png" alt="Logs in a docker container"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I hope that would save you some time. Happy logging!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This is a cross post &lt;a href="https://asizikov.github.io/2020/07/10/azure-function-docker-log/"&gt;from my personal blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>azure</category>
      <category>azurefunctions</category>
      <category>docker</category>
      <category>logging</category>
    </item>
    <item>
      <title>Provision your Azure environment with Terraform and Octopus Deploy</title>
      <dc:creator>Anton Sizikov</dc:creator>
      <pubDate>Sun, 22 Mar 2020 09:58:18 +0000</pubDate>
      <link>https://dev.to/asizikov/provision-your-azure-environment-with-terraform-and-octopus-deploy-39a6</link>
      <guid>https://dev.to/asizikov/provision-your-azure-environment-with-terraform-and-octopus-deploy-39a6</guid>
      <description>&lt;p&gt;My current project has reached the point where we have to manage our infrastructure in a more organized way rather than ad-hoc manual configurations.&lt;/p&gt;

&lt;h3&gt;
  
  
  Current landscape
&lt;/h3&gt;

&lt;p&gt;We use a pretty standard setup for a .NET web application.&lt;/p&gt;

&lt;p&gt;We keep our code in one monorepo hosted in internal GitLab. We use TeamCity to build our code and we use Octopus Deploy to run our deployments to Azure.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--IBIn0PH_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://asizikov.github.io/images/2020-03-terraform-octopus/current-setup.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--IBIn0PH_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://asizikov.github.io/images/2020-03-terraform-octopus/current-setup.JPG" alt="Current setup"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We have several environments (let's call them &lt;code&gt;feature&lt;/code&gt;, &lt;code&gt;test&lt;/code&gt;, and &lt;code&gt;prod&lt;/code&gt;) and they all are different. Each one has a different configuration, different accounts, different topology, different numbers of replicas and shards, you name it.&lt;/p&gt;

&lt;p&gt;Things like connection strings, account and passwords are managed by Octopus.&lt;/p&gt;

&lt;p&gt;At this point, we would like to utilize the configuration management feature provider by Octopus, and that's why we would have to jump through several hoops to marry terraform configs and Octopus variable substitution mechanism.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scope of the problem
&lt;/h3&gt;

&lt;p&gt;We'd like to move to an Infrastructure-as-a-Code approach and provisioning our system automatically as a part of our CI/CD pipeline.&lt;/p&gt;

&lt;p&gt;To do that we're going to add &lt;a href="https://www.terraform.io/"&gt;Terraform&lt;/a&gt; to the game.&lt;/p&gt;

&lt;p&gt;At the time I'm writing this post the versions for the software are the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;TeamCity 2019.2.2&lt;/li&gt;
&lt;li&gt;Octopus Deploy v2019.12.0 LTS&lt;/li&gt;
&lt;li&gt;Terraform v0.12.24&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;azurerm&lt;/code&gt; provider 2.2.0&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Repository Setup
&lt;/h3&gt;

&lt;p&gt;This part is not very important, but to provide you with the full picture I'll put it here.&lt;/p&gt;

&lt;p&gt;We keep our Terraform files in the same repository alongside the application code and database schemas. Basically, it's just an &lt;code&gt;\infrastructure&lt;/code&gt; directory in the root of the repo.&lt;/p&gt;

&lt;h3&gt;
  
  
  Terraform Setup
&lt;/h3&gt;

&lt;p&gt;We run our application in Azure, hence the choice of Terraform backend and a provider: we keep &lt;a href="https://www.terraform.io/docs/state/index.html"&gt;Terraform state&lt;/a&gt; in Azure. &lt;/p&gt;

&lt;p&gt;Please refer to &lt;a href="https://docs.microsoft.com/en-us/azure/terraform/terraform-backend"&gt;the official documentation&lt;/a&gt; for a detailed step by step guide on setting up storage for Terraform State.&lt;/p&gt;

&lt;p&gt;In short, you'd need to run the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="nv"&gt;RESOURCE_GROUP_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;terraformstate-rg
&lt;span class="nv"&gt;STORAGE_ACCOUNT_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;terraformstateci
&lt;span class="nv"&gt;CONTAINER_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;terraformstate

&lt;span class="c"&gt;# Create resource group&lt;/span&gt;
az group create &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="nv"&gt;$RESOURCE_GROUP_NAME&lt;/span&gt; &lt;span class="nt"&gt;--location&lt;/span&gt; eastus

&lt;span class="c"&gt;# Create storage account&lt;/span&gt;
az storage account create &lt;span class="nt"&gt;--resource-group&lt;/span&gt; &lt;span class="nv"&gt;$RESOURCE_GROUP_NAME&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="nv"&gt;$STORAGE_ACCOUNT_NAME&lt;/span&gt; &lt;span class="nt"&gt;--sku&lt;/span&gt; Standard_LRS &lt;span class="nt"&gt;--encryption-services&lt;/span&gt; blob

&lt;span class="c"&gt;# Get storage account key&lt;/span&gt;
&lt;span class="nv"&gt;ACCOUNT_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;az storage account keys list &lt;span class="nt"&gt;--resource-group&lt;/span&gt; &lt;span class="nv"&gt;$RESOURCE_GROUP_NAME&lt;/span&gt; &lt;span class="nt"&gt;--account-name&lt;/span&gt; &lt;span class="nv"&gt;$STORAGE_ACCOUNT_NAME&lt;/span&gt; &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;0].value &lt;span class="nt"&gt;-o&lt;/span&gt; tsv&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Create blob container&lt;/span&gt;
az storage container create &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="nv"&gt;$CONTAINER_NAME&lt;/span&gt; &lt;span class="nt"&gt;--account-name&lt;/span&gt; &lt;span class="nv"&gt;$STORAGE_ACCOUNT_NAME&lt;/span&gt; &lt;span class="nt"&gt;--account-key&lt;/span&gt; &lt;span class="nv"&gt;$ACCOUNT_KEY&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"storage_account_name: &lt;/span&gt;&lt;span class="nv"&gt;$STORAGE_ACCOUNT_NAME&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"container_name: &lt;/span&gt;&lt;span class="nv"&gt;$CONTAINER_NAME&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"access_key: &lt;/span&gt;&lt;span class="nv"&gt;$ACCOUNT_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Once you've successfully created a storage for your state you can update your &lt;code&gt;main.tf&lt;/code&gt; file with the backend configuration:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;For the purpose of this blog post I'll be creating just a new resource group with one SQL server.&lt;/p&gt;

&lt;p&gt;Let's declare our setup in a new &lt;code&gt;sql.tf&lt;/code&gt; file: &lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;note that I'm using variables in a gist above. &lt;/p&gt;

&lt;p&gt;Variables are defined in a &lt;code&gt;variables.tf&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"sql_server_admin_pwd"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;This works fine, but for the local testing I keep the &lt;code&gt;terraform.tfvars&lt;/code&gt; files with the values:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource_group_name&lt;/span&gt;  &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"test_rg"&lt;/span&gt;
&lt;span class="nx"&gt;sql_server_admin_name&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"admin_name"&lt;/span&gt;
&lt;span class="nx"&gt;sql_server_admin_pwd&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"P@$$w0rd1!"&lt;/span&gt; &lt;span class="c1"&gt;# dummy pwd used for local tests&lt;/span&gt;

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


&lt;p&gt;values are not encrypted, but they are just for the local tests and experiments. They will be replaced with the environment-specific vars by Octopus later.&lt;/p&gt;
&lt;h3&gt;
  
  
  TeamCity Setup
&lt;/h3&gt;

&lt;p&gt;Octopus has a specific syntax for variable substitution. Usually, it looks for &lt;code&gt;#{Variable}&lt;/code&gt; markers in your config files. As you can imagine they are not always valid as resource names.&lt;/p&gt;

&lt;p&gt;As I mentioned before our &lt;code&gt;terraform.tfvars&lt;/code&gt; has realistic values for local tests. It helps our infrastructure engineers verifying their work by running &lt;code&gt;terraform plan&lt;/code&gt; commands.&lt;/p&gt;

&lt;p&gt;However, when we build our packages we want to convert &lt;code&gt;tfvars&lt;/code&gt; file to replace variable's values with the syntax suitable for Octopus.&lt;/p&gt;

&lt;p&gt;I had to invent a convention here.&lt;/p&gt;

&lt;p&gt;Every variable declaration &lt;code&gt;var_name = "abc123"&lt;/code&gt; will be converted to a  &lt;code&gt;var_name = "#{var_name}"&lt;/code&gt; line.&lt;/p&gt;

&lt;p&gt;To do that we've set up a build step with the following PowerShell script:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;After that we can build our code and then create a release package with our Terraform config.&lt;/p&gt;

&lt;p&gt;A TeamCity build configuration has a custom &lt;code&gt;%octopus.package.suffix%&lt;/code&gt; variable declared. This is a suffix that is composed of the branch name. It's empty for the master branch build and has something like &lt;code&gt;feature-AB-123&lt;/code&gt; for a feature branch with an &lt;code&gt;AB-123&lt;/code&gt; name. Yes, this is a JIRA ticket name :) &lt;/p&gt;

&lt;p&gt;With this variable we can prepare a zipped artifacts file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;+:infrastructure/** =&amp;gt; Terraform.%build.number%%octopus.package.suffix%.zip
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;that would produce a file &lt;code&gt;Terraform.1.123.9-feature-AB-123&lt;/code&gt; which will later be uploaded to Octopus.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9PPezdYF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://asizikov.github.io/images/2020-03-terraform-octopus/team-city-pipeline.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9PPezdYF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://asizikov.github.io/images/2020-03-terraform-octopus/team-city-pipeline.JPG" alt="TeamCity pipeline"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Octopus Setup
&lt;/h3&gt;

&lt;p&gt;Let's create a new Step in Octopus project:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4o-KTjop--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://asizikov.github.io/images/2020-03-terraform-octopus/octopus-step.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4o-KTjop--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://asizikov.github.io/images/2020-03-terraform-octopus/octopus-step.png" alt="Octopus Step"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We currently use an official &lt;a href="https://octopus.com/docs/deployment-examples/terraform-deployments/apply-terraform"&gt;Apply a Terraform template&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;However, there are few things which we had to configure in order to make it work for us.&lt;/p&gt;

&lt;p&gt;First of all, Octopus cannot keep up with Terraform development, so you may need to update a &lt;code&gt;terraform.exe&lt;/code&gt; version yourself. Just grab the latest version from &lt;a href="https://www.terraform.io/downloads.html"&gt;Terraform Downloads page&lt;/a&gt; and put it in a folder on your Octopus server. Something like &lt;code&gt;C:\Program Files\Terraform&lt;/code&gt; would do.&lt;/p&gt;

&lt;p&gt;Then you need to provide a path to your custom &lt;code&gt;terraform.exe&lt;/code&gt; executable. To do that you need to set up a variable: &lt;code&gt;Octopus.Action.Terraform.CustomTerraformExecutable&lt;/code&gt;. Give it a &lt;code&gt;C:\Program Files\Terraform\terraform.exe&lt;/code&gt; value.&lt;/p&gt;

&lt;p&gt;As you remember the release package contains a &lt;code&gt;terraform.tfvars&lt;/code&gt; file with templates in it. So don't forget to tick this box:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--w9AD6F8b--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://asizikov.github.io/images/2020-03-terraform-octopus/octopus-replace.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--w9AD6F8b--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://asizikov.github.io/images/2020-03-terraform-octopus/octopus-replace.png" alt="Octopus Step"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And of course, you will have to define your variables for each environment:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Hj5DQATa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://asizikov.github.io/images/2020-03-terraform-octopus/octopus-variables.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Hj5DQATa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://asizikov.github.io/images/2020-03-terraform-octopus/octopus-variables.png" alt="Octopus Step"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;note the variable name format. It should be the same as the variable names in your &lt;code&gt;terraform.tfvars&lt;/code&gt; file. Here we can have different values for each environment. Also, we store passwords encrypted.&lt;/p&gt;

&lt;p&gt;And now we have a new player in our CI/CD pipeline: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2JM7Pxhv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://asizikov.github.io/images/2020-03-terraform-octopus/result.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2JM7Pxhv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://asizikov.github.io/images/2020-03-terraform-octopus/result.JPG" alt="Octopus Step"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Crossposting from my &lt;a href="https://asizikov.github.io/2020/03/21/azure-terraform-octopus/"&gt;personal blog&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>azure</category>
      <category>terraform</category>
      <category>octopusdeploy</category>
      <category>configurationasacode</category>
    </item>
    <item>
      <title>Should I do that test assignment?</title>
      <dc:creator>Anton Sizikov</dc:creator>
      <pubDate>Thu, 22 Nov 2018 19:03:09 +0000</pubDate>
      <link>https://dev.to/asizikov/thoughts-on-test-assignments-3o0j</link>
      <guid>https://dev.to/asizikov/thoughts-on-test-assignments-3o0j</guid>
      <description>&lt;p&gt;Test assignments aren't really a good thing. In fact, it's quite the opposite. It might be one of the worst things that have happened to the hiring process.&lt;/p&gt;

&lt;p&gt;The hiring process, in general, is quite broken in many senses. It's not really clear how to hire an engineer. New ways of testing our skills come and go. It shows us that it's nearly impossible to check interviewee skills in 30-90 minutes time frame. &lt;/p&gt;

&lt;p&gt;I'm not going to talk about different puzzlers or tricky questions about the compiler, nor I'm going to cover any stress-interview practices here. Today is all about test assignments. &lt;/p&gt;

&lt;h1&gt;
  
  
  From a company's point of view
&lt;/h1&gt;

&lt;p&gt;Hiring is a very expensive process and it's not very reliable either. Hours of interviews ain't free at all:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An hour of one or two engineers conducting an interview&lt;/li&gt;
&lt;li&gt;20-30 minutes to prepare before, and to compose results after an interview&lt;/li&gt;
&lt;li&gt;Recruiter's time&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you sum it up, you'll end up with a good number. And it's fair to expect the business to cut the costs. There are many ways to make it cheaper: phone screening and tests are very typical in the industry.&lt;/p&gt;

&lt;p&gt;And there is one way which outstands the others. An assignment which is given to the candidate. Typically it's a piece of rather boring code to write with a possible trick in it. Sometimes you'd have to do it even before a phone screening phase as an extra filter. And most of the time you take it before an on-site round.&lt;/p&gt;

&lt;p&gt;Often it's assumed that you would spend (should I say waste?) a few hours to finish up the assignment. That's an estimate made by the hiring manager or an engineer, and it's based on the assumption that you are familiar with every tech and every aspect of the task. &lt;/p&gt;

&lt;p&gt;How long does it take to check your code? Well, it's about 15-20 minutes. Doesn't look fair, right?&lt;/p&gt;

&lt;p&gt;That's correct. A company is trying to put the expenses on your name. They don't know how to evaluate your skills and your experience during a short interview, nor they know how to do that by phone, hence the need for you to spend hours in order to convince them simply to talk.&lt;/p&gt;

&lt;p&gt;By the way, a person whose job is to evaluate your task is not going to give it too much effort. They have enough on their plate and they don't really like switching their attention. Imagine looking at the same code over an over again. Often they would quickly go through a checklist.&lt;/p&gt;

&lt;h1&gt;
  
  
  A candidate's point of view
&lt;/h1&gt;

&lt;p&gt;Unless you're aiming for a top tech company like Google, Amazon or Facebook (they don't really give test assignments out, btw), you will not be dying to get a particular job. A vast majority of companies out there are doing similar things: storing the form's input in a DB. Well, sort of, but you know what I mean.&lt;/p&gt;

&lt;p&gt;For the candidate test assignments are not very useful. On one hand, often they are similar to each other, on the other hand, they are hard to reuse. Every company is trying to make it a bit special. And you have to start a new project, configure all the parts, pick an ORM or set up a linter, over and over again.&lt;/p&gt;

&lt;p&gt;Speaking of... How often do you start a new project at work? Like from the very beginning. You know, a &lt;code&gt;git init&lt;/code&gt; and &lt;code&gt;File-&amp;gt;New Project&lt;/code&gt;. I've done so a couple dozen times doing various test assignments, and handfull times at work. Most of the time you either join an existing project or start one and keep working with it for a long period. So, honestly, it doesn't look like a relevant skill to me.&lt;/p&gt;

&lt;h1&gt;
  
  
  How much does your time cost?
&lt;/h1&gt;

&lt;p&gt;Think about it. How many man-hours do you have on an overage day? Well, if you're young and single, you might even have all 24 of them. You can't keep it up this way for a long time, but you can make it for a day or two.&lt;br&gt;
However, it's a different story when you're older or have a family. You have like what? 12-14 hours a day. The remaining part of the day you do your chores and sleep.&lt;/p&gt;

&lt;p&gt;You have to spend from eight to ten hours at work and commuting. That leaves you about four spare hours to use. So a hiring manager is going to take those four priceless hours. They want you to spend all your available resources so that they can save some time on their side.&lt;/p&gt;

&lt;p&gt;How many resources does a company have? There are tens of engineers, managers and HR specialists there. If we speak about a small to middle size company. So they have hundreds of man-hours at their disposal. An extra 20-40 minutes to evaluate a candidate doesn't seem to be too crucial.&lt;/p&gt;

&lt;p&gt;And that is an obvious asymmetry in the process. You have less time than the company, but you have to spend more resources upfront.&lt;/p&gt;

&lt;p&gt;I don't really know what should we do about it. It's up to each individual. If you feel confident if you value your time and you're applying to an overage company you may refuse taking an assignment home. There are many ways to do so, you may ask them to pay for your time. You may offer them to take a look at your portfolio first. Show them your blog, your public projects if you have any. Let them download an app you built and try it first. Whatever works for you.&lt;/p&gt;

&lt;p&gt;However, you might not be in a position to refuse. You can simply be running out of money or you might be talking to a dream job and you don't want to ruin it over the pride. I understand that.&lt;/p&gt;

&lt;p&gt;Though I recommend you to publish the assignment somewhere. Let it be GitHub if we talk about code. Or Dribble or any other place which is popular among specialist in your field. Just be clear about it upfront: you are going to publish it because you own the result of your work. Unless they want to buy it out. &lt;/p&gt;

&lt;p&gt;Hopefully one day you'll be offered with a similar assignment. Just send them a link back. It might work and could probably save you some time.&lt;/p&gt;

&lt;h1&gt;
  
  
  It can't be that bad, can it?
&lt;/h1&gt;

&lt;p&gt;Well... Let's see.&lt;br&gt;
Do you remember that time when you've spend many hours, build a perfect thing, neat and polished? And you were hired right after that with no questions asked whatsoever. Ugh, that was just a dream.&lt;/p&gt;

&lt;p&gt;Unfortunately, often a perfect result will simply be a reason to meet you in person. And that's where an actual interview will take its place. They would ask you some questions to make sure that you are the author. Sometimes it feels like an unnecessary step to go through. They could have given you a simple 15-20 minutes test over the phone. &lt;/p&gt;

&lt;p&gt;Imagine you found that you don't like the office, the team or anything else about the company. You know, there are things with you only can find out during a face-to-face phase. And in order to get there, you've spent hours working on that test assignment. What a waste...&lt;/p&gt;

&lt;p&gt;One more reason in favor of test assignments. They should equalize candidates who have time for a public activity like blogging or OSS contributions and people with family or those who cannot publish any code due to their work contract limitations. Honestly, I don't see how that helps. If you have no time to work on your pet project why would you have time to work on a test assignment? &lt;/p&gt;

&lt;p&gt;What do you think about it? Do you often see a take-home assignment given before the actual on-site interview?&lt;/p&gt;

&lt;p&gt;This text was originally published in Russian &lt;a href="https://teletype.in/@your_soft_skillzz/H1tx4p-n7"&gt;in my blog&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>jobsearch</category>
      <category>career</category>
      <category>programmers</category>
      <category>discuss</category>
    </item>
    <item>
      <title>You are mocking it wrong.</title>
      <dc:creator>Anton Sizikov</dc:creator>
      <pubDate>Wed, 27 Dec 2017 22:03:20 +0000</pubDate>
      <link>https://dev.to/asizikov/you-are-mocking-it-wrong-5gh3</link>
      <guid>https://dev.to/asizikov/you-are-mocking-it-wrong-5gh3</guid>
      <description>&lt;p&gt;Well, probably &lt;em&gt;you&lt;/em&gt; are not, but let me grumble a little bit anyway.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fasizikov.github.io%2Fimages%2F2017-12-mocking%2Fmockingbird.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fasizikov.github.io%2Fimages%2F2017-12-mocking%2Fmockingbird.jpg" alt="Mockingbird picture"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Mockingbird knows how to mock.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I've been working with various code bases throughout of my career, and there is one pattern which I see rather often. As you may have already guessed it's the unit-tests and mocking I'm going to talk about here. To give it a nice catchy start, I'd claim here, that mocks should be used when you &lt;em&gt;have to&lt;/em&gt;, but not when you &lt;em&gt;can&lt;/em&gt;. &lt;/p&gt;

&lt;p&gt;I'll give a few examples of somewhat useless and even harmful tests.&lt;br&gt;&lt;br&gt;
All the examples are going to be made up, but I hope you'd get the point. &lt;/p&gt;

&lt;p&gt;So, let's start with the typical &lt;code&gt;Greeter&lt;/code&gt; example. This is a "Hello, world" of Unit Testing. In one way or another, this sample gets repeated in all the articles, posts and books dedicated to unit-tests and mocking frameworks.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;IGreeter&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;Greet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Greeter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IGreeter&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;Greet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"Hello,"&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="s"&gt;" "&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;IGreeter&lt;/span&gt; &lt;span class="n"&gt;_greeter&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IGreeter&lt;/span&gt; &lt;span class="n"&gt;greeter&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
    &lt;span class="n"&gt;_greeter&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;greeter&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;FormatPageHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"&amp;lt;h&amp;gt;"&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt;  &lt;span class="nf"&gt;_greeter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"&amp;lt;/h&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;  
&lt;span class="p"&gt;}&lt;/span&gt;

 &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ClientTests&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;pubic&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Test&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;mock&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Mock&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IGreeter&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
        &lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;greeter&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;greeter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Greet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"John"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Mr."&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;Returns&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello, Mr. John"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;FormatPageHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"John"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Mr."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;Assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AreEquals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"&amp;lt;h&amp;gt;Hello, Mr. John&amp;lt;/h&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;//All good here, what does it test, tho?&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; 
 &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So far so good. Tests are green. The Greeter interface isn't perfect, though. Two &lt;code&gt;strings&lt;/code&gt; as a parameter? So easy to mess up, isn't it? Most probably you have a method like that in your project. &lt;/p&gt;

&lt;p&gt;Ok then, imagine that you decided to make this method less error prone. You don't have much time for a proper refactoring because you have a feature to work on. Let's just swap the parameters. It's much more natural for a human to put the title before the name. Depends on the culture, I know. &lt;/p&gt;

&lt;p&gt;("John", "Mr.") is more awkward compared to ("Mr.", "John") and ("Dr.", "Smith"). &lt;/p&gt;

&lt;p&gt;So, we'll end up with the greeter like that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;//Version 2&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Greeter2&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IGreeter&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;Greet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"Hello, "&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="s"&gt;" "&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; 

&lt;span class="n"&gt;IGreeter&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;Greet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;add.&lt;/code&gt;, &lt;code&gt;commit&lt;/code&gt;, &lt;code&gt;push&lt;/code&gt;.  Our beloved build server will pick the changes up, will run the tests and will fail of course. We forgot to update the test for the &lt;code&gt;IGreeter&lt;/code&gt; implementation. Once they fixed we're good, aren't we?&lt;/p&gt;

&lt;p&gt;Not really, remember the &lt;code&gt;Client&lt;/code&gt; class? Now it's incorrect, we still pass the title and the name in the old order, even though our test claims that everything is fine.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fasizikov.github.io%2Fimages%2F2017-12-mocking%2Ffine.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fasizikov.github.io%2Fimages%2F2017-12-mocking%2Ffine.jpg" alt="This is fine"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here is the paradox, we introduced mocking so that we can test our class in isolation, but we had to put some code to make that mock return something. It's still the logic, isn't it? Now &lt;code&gt;IGreeter&lt;/code&gt; has at least two implementations. Most probably in your codebase, you have tens of implementations for the mocked class. Just because it's a very much reused dependency, and you have to mock it all over again. &lt;/p&gt;

&lt;p&gt;We can improve that test, tune it, but the only way we can make it fail is to repeat the &lt;code&gt;Greeter&lt;/code&gt; logic in our mock set up. But wait a minute, if we have the same implementation, why don't we reuse the existing code? &lt;/p&gt;

&lt;p&gt;The more complex your mocked object is the more complex your mock setup becomes. If that's not the case, most probably you're testing just the interaction with your dumb mock, i.e. you're not testing anything. Just like the &lt;code&gt;ClientTest&lt;/code&gt; above. The only positive outcome is the extra 20c which your company pays to Amazon for that CPU time you waisted on the build server.&lt;/p&gt;

&lt;p&gt;It's not rare to have more than one dependency. I can imagine that our &lt;code&gt;Client&lt;/code&gt; could use some &lt;code&gt;IHmlRenderer&lt;/code&gt; which would consume the &lt;code&gt;IGreeter&lt;/code&gt; result. You would mock that one too, right? &lt;/p&gt;

&lt;p&gt;Sweet as. You now have a test which verifies how two mocks are integrated with each other. How does that prove your code correctness, thought? I don't know. &lt;/p&gt;

&lt;p&gt;I'm going to step back now, and talk about it from another point of view. What does the typical system look like? I assume, it's not the bunch of isolated classes, but it's more like a spiderweb of dependencies. If you look at type dependency diagram you would see a group of clusters, where each cluster is a somewhat tightly coupled set of classes. This is how we manage complexity, we break down one large class into small pieces, but those pieces would never be used in isolation. Each of them would be responsible for a little piece of work. You would write a separate test suite for them, mock out all the dependencies (they all belong to the same cluster). &lt;/p&gt;

&lt;p&gt;How is that different to the attempt to &lt;a href="https://stackoverflow.com/questions/34571/how-do-i-test-a-private-function-or-a-class-that-has-private-methods-fields-or" rel="noopener noreferrer"&gt;test a private method&lt;/a&gt;? &lt;/p&gt;

&lt;p&gt;That little convenience wrapper for the standard library class is nothing more than an implementation detail. &lt;/p&gt;

&lt;h3&gt;
  
  
  But integration tests are slow...
&lt;/h3&gt;

&lt;p&gt;That would be an expected note. If we come back to our &lt;code&gt;ClientTest&lt;/code&gt;. What performs better, the test with mock, or the test with the concrete Greeter implementation? I guess the answer is obvious. We shouldn't forget that that test doesn't prove anything. It is slower, it allocates more, and it's wrong. I'd say it's harmful. &lt;/p&gt;

&lt;h3&gt;
  
  
  So you're going to send out those emails every time you test?
&lt;/h3&gt;

&lt;p&gt;Ok, that is a good question. Remember I said that we should mock when we &lt;em&gt;have to&lt;/em&gt;, not when we &lt;em&gt;can&lt;/em&gt;? That's exactly the right situation for the test isolation.&lt;/p&gt;

&lt;p&gt;You don't want to make HTTP calls while running your unit tests suite, you don't want to send out emails, neither do I. &lt;/p&gt;

&lt;h3&gt;
  
  
  Hey, I tried not to mock, and I got sick of initing all the dependencies
&lt;/h3&gt;

&lt;p&gt;This is where it gets painful. I saw systems like that, it's a nightmare to maintain. Manually setting up the dependency of the dependency is really not the way to go. It's hard, and everyone would avoid writing a new test. But that's an easy task to solve. &lt;/p&gt;

&lt;p&gt;How do we build a dependency tree in the runtime? I hope you're using the DI framework which wires up interfaces and implementations together, it knows how to create a new dependency, it knows when and how to rebuild a dependency tree. If you're building a web service you should reset the state between requests, so keep as many dependencies request scoped as it's possible. &lt;/p&gt;

&lt;p&gt;The same pattern is applicable for your test suite. Everything is request scoped and stateless already, so treat each test as a 'request' and let the DI container to build the system under test for you. &lt;/p&gt;

&lt;p&gt;Of course, you would have to set up the DI container differently. &lt;/p&gt;

&lt;p&gt;Module (or cluster) level test would build the dependency tree for that module but would mock out the rest of the world. Like no HTTP calls, no DB, no emails, you've got the point.&lt;/p&gt;

&lt;p&gt;At this point, you would realize that you don't need that test which verifies that your utility class can split the string, but you would be sure that your &lt;code&gt;MortgageCalculator&lt;/code&gt; does the job. After all, that is your business rule, and that is the feature you're building. Unless you are the low-level framework developer of course. Most of us are not, though.&lt;/p&gt;

&lt;p&gt;Once you've got all the clusters well tested, all the interfaces established you may try to break it down a little bit. Or you may want to leave it as is, it's up to you. &lt;/p&gt;

&lt;p&gt;Imagine you want to extract some logic into the separate class. Now your system has a new dependency, but that logic has already been tested. You don't need to set up a new mock, you don't need to repeat the logic of the extracted class in your test set up, you don't need to update the test to instantiate the tested class. It's just there, and your safety net still works. If you extract and modify the logic, you would break the test.  Do you see the beauty? Extracting the class and introducing a new dependency is as easy as moving the logic into a private method.&lt;/p&gt;

&lt;h3&gt;
  
  
  Summary
&lt;/h3&gt;

&lt;p&gt;The approach above would give you the following benefits: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fewer meaningful, useless and harmful tests&lt;/li&gt;
&lt;li&gt;No need to maintain duplicated implementation (mock set ups)&lt;/li&gt;
&lt;li&gt;Feature driven tests, tests that verify that the cluster does the job&lt;/li&gt;
&lt;li&gt;An easy to refactor code base&lt;/li&gt;
&lt;li&gt;You can improve module level access (no need to make the class public just for the testing)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;From my experience tests, based on that cluster approach, are much more reliable. When they break, they actually mean that the system is dysfunctional, when they pass you can be sure that you did not break the logic. &lt;/p&gt;

&lt;p&gt;We still have to run integration tests to be sure that the third party integrations are working, that the system's runtime configuration is valid. &lt;/p&gt;

&lt;p&gt;I am more than open to any criticism, feel free to tear that post apart. :)&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Crossposting from my &lt;a href="https://asizikov.github.io/2017/12/23/you-are-mocking-it-wrong/" rel="noopener noreferrer"&gt;personal blog&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>unittesting</category>
      <category>mocking</category>
      <category>testing</category>
      <category>tdd</category>
    </item>
  </channel>
</rss>
