<!DOCTYPE html><html><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1.0"/><title>Ryan Cromwell</title><meta name="description" content="Ryan Cromwell - Big Vision. Small Changes. Learning by shipping."/><link href="https://twitter.com/cromwellryan" rel="me"/><link href="https://github.com/cromwellryan" rel="me"/><meta name="twitter:card" content="summary"/><meta name="twitter:site" content="@cromwellryan"/><meta name="twitter:title" content="Cromwellhaus.com"/><meta name="twitter:description" content="Ryan Cromwell: Dad of 4. Technical Director @hearsparkbox. Big vision. Small changes. Always Learning."/><link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png"/><link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png"/><link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png"/><link rel="manifest" href="/site.webmanifest"/><link rel="canonical" href="https://cromwellhaus.com"/><meta name="next-head-count" content="15"/><link rel="preload" href="/_next/static/css/c468b5f43261d9cb2952.css" as="style"/><link rel="stylesheet" href="/_next/static/css/c468b5f43261d9cb2952.css" data-n-g=""/><link rel="preload" href="/_next/static/css/1808f6189e86d119eba6.css" as="style"/><link rel="stylesheet" href="/_next/static/css/1808f6189e86d119eba6.css" data-n-p=""/><noscript data-n-css=""></noscript><link rel="preload" href="/_next/static/chunks/webpack-189c53927ffd3caf09c3.js" as="script"/><link rel="preload" href="/_next/static/chunks/framework-92300432a1172ef1338b.js" as="script"/><link rel="preload" href="/_next/static/chunks/main-71948af4b0f09c0fc30e.js" as="script"/><link rel="preload" href="/_next/static/chunks/pages/_app-44a7fcb1aeab50f8aaeb.js" as="script"/><link rel="preload" href="/_next/static/chunks/203-806adfe910971c6684ee.js" as="script"/><link rel="preload" href="/_next/static/chunks/pages/index-1a1a0750ec0c7ead8ae3.js" as="script"/></head><body><div id="__next"><div><header class="header"><div class="header__container"><h1 class="header__link-home"><a aria-label="Link to Home" class="header__link-home" title="Link to Home" href="/">Ryan Cromwell</a></h1><nav class="header__nav"><a class="nav__link" href="/archive">Archive</a><a class="nav__link" href="/about">About</a></nav></div></header><main class="body-content"><div class="body-container"><section><h2>Recent thoughts, talks, and publications</h2><article class="Article_article__1j6Zw"><h2 class="Article_title__Siuk2"><a class="Article_link__2OWck" href="/leaving-aws-subaccounts-behind">Using Terraform to automate the creation and deprovisioning of AWS Member Accounts</a></h2>Using Terraform to automate AWS member accounts for developers and dealing with off-boarding those team members can be hindered by the inability to easily delete member accounts. I show we separate the off-boarding of team members from the need to delete member accounts to make this simple and automatic.</article><article class="Article_article__1j6Zw"><h2 class="Article_title__Siuk2 Article_externalAnnotation__3nE0c"><a class="Article_link__2OWck" href="https://sparkbox.com/foundry/successful_software_build_and_deployment_pipelines">Traits of a Build and Deployment Pipeline</a></h2><p>I believe that a team&#x27;s release cadence has an incredible impact on their success and enjoyment. I wrote a little about the traits that make up a successful build and deploy pipeline for our team at Sparkbox.</p></article><article class="Article_article__1j6Zw"><h2 class="Article_title__Siuk2 Article_externalAnnotation__3nE0c"><a class="Article_link__2OWck" href="https://youtu.be/Lx8BepRpfvg">Making the Leap to Tech Lead</a></h2><p>Building a healthy, sustainable development organization involves defining technical leadership. In 2019, I shared my thoughts on the tech lead role at the Solidus conference in Utah. Shortly after, I hiked City Creek Canyon and fell in love with the area.</p></article></section></div></main><section class="prefooter"><div class="prefooter__container"><div class="onecoolthing"><h3 class="onecoolthing__header">Cool <a href="https://github.com/containrrr/watchtower" class="util-highlight">Tech</a> Thing</h3><p class="onecoolthing__body"><a href="https://github.com/containrrr/watchtower">Watchtower</a> has me excited for a Heroku for Docker. This is a big deal if you know <a href="https://sparkbox.com/foundry/successful_software_build_and_deployment_pipelines">how I feel about deploy pipelines</a>.</p></div><div class="onecoolthing"><h3 class="onecoolthing__header">Latest <a class="util-highlight" href="https://twitter.com/cromwellryan">Tweet</a></h3><p class="onecoolthing__body"><a class="twitter-timeline" href="https://twitter.com/cromwellryan" data-height="200" data-widget-id="368846062565347328" data-chrome="noheader nofooter noborders noscrollbar transparent" data-tweet-limit="1">Tweets by @cromwellryan</a></p><script async="" src="https://platform.twitter.com/widgets.js"></script></div></div></section><footer class="footer"><div class="footer__container">©2021 Ryan Cromwell<nav class="footer__social"><a class="footer__social--link" href="https://www.linkedin.com/in/cromwellryan/"><svg xmlns="http://www.w3.org/2000/svg" width="24px" height="24px" viewBox="0 0 24 24"><path d="M19 0h-14c-2.761 0-5 2.239-5 5v14c0 2.761 2.239 5 5 5h14c2.762 0 5-2.239 5-5v-14c0-2.761-2.238-5-5-5zm-11 19h-3v-11h3v11zm-1.5-12.268c-.966 0-1.75-.79-1.75-1.764s.784-1.764 1.75-1.764 1.75.79 1.75 1.764-.783 1.764-1.75 1.764zm13.5 12.268h-3v-5.604c0-3.368-4-3.113-4 0v5.604h-3v-11h3v1.765c1.396-2.586 7-2.777 7 2.476v6.759z"></path></svg></a><a class="footer__social--link" href="https://github.com/cromwellryan"><svg width="24px" height="24px" viewBox="0 0 1024 1024" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M8 0C3.58 0 0 3.58 0 8C0 11.54 2.29 14.53 5.47 15.59C5.87 15.66 6.02 15.42 6.02 15.21C6.02 15.02 6.01 14.39 6.01 13.72C4 14.09 3.48 13.23 3.32 12.78C3.23 12.55 2.84 11.84 2.5 11.65C2.22 11.5 1.82 11.13 2.49 11.12C3.12 11.11 3.57 11.7 3.72 11.94C4.44 13.15 5.59 12.81 6.05 12.6C6.12 12.08 6.33 11.73 6.56 11.53C4.78 11.33 2.92 10.64 2.92 7.58C2.92 6.71 3.23 5.99 3.74 5.43C3.66 5.23 3.38 4.41 3.82 3.31C3.82 3.31 4.49 3.1 6.02 4.13C6.66 3.95 7.34 3.86 8.02 3.86C8.7 3.86 9.38 3.95 10.02 4.13C11.55 3.09 12.22 3.31 12.22 3.31C12.66 4.41 12.38 5.23 12.3 5.43C12.81 5.99 13.12 6.7 13.12 7.58C13.12 10.65 11.25 11.33 9.47 11.53C9.76 11.78 10.01 12.26 10.01 13.01C10.01 14.08 10 14.94 10 15.21C10 15.42 10.15 15.67 10.55 15.59C13.71 14.53 16 11.53 16 8C16 3.58 12.42 0 8 0Z" transform="scale(64)" fill="#1B1F23"></path></svg></a></nav></div></footer></div></div><script id="__NEXT_DATA__" type="application/json">{"props":{"pageProps":{"recentContent":[{"metadata":{"tags":[],"concepts":[]},"sys":{"space":{"sys":{"type":"Link","linkType":"Space","id":"w95trmn9jf9t"}},"id":"5iKEnYgpVkq0ovIyOf9yra","type":"Entry","createdAt":"2021-08-06T19:15:41.672Z","updatedAt":"2021-08-09T14:29:00.697Z","environment":{"sys":{"id":"master","type":"Link","linkType":"Environment"}},"publishedVersion":199,"revision":6,"contentType":{"sys":{"type":"Link","linkType":"ContentType","id":"post"}},"locale":"en-US"},"fields":{"slug":"leaving-aws-subaccounts-behind","abstract":"Using Terraform to automate AWS member accounts for developers and dealing with off-boarding those team members can be hindered by the inability to easily delete member accounts. I show we separate the off-boarding of team members from the need to delete member accounts to make this simple and automatic.","title":"Using Terraform to automate the creation and deprovisioning of AWS Member Accounts","body":{"nodeType":"document","data":{},"content":[{"nodeType":"paragraph","content":[{"nodeType":"text","value":"We ","marks":[],"data":{}},{"nodeType":"hyperlink","content":[{"nodeType":"text","value":"automate provisioning developer AWS sandbox accounts","marks":[],"data":{}}],"data":{"uri":"https://github.com/sparkbox/aws-infrastructure/tree/main/modules/development-team"}},{"nodeType":"text","value":" for our team at Sparkbox. These accounts are paid for by the parent organization. This gives our team full access to learn and prototype with all of AWS' services. We use Billing threshold notifications to keep things from spinning out of control.","marks":[],"data":{}}],"data":{}},{"nodeType":"paragraph","content":[{"nodeType":"text","value":"It's relatively easy to create the organizations, users, and AWS accounts. The code fits in a nice module. The challenge we face comes when a team member leaves. When they leave, we need to quickly remove access to Sparkbox resources per our security policy.","marks":[],"data":{}}],"data":{}},{"nodeType":"paragraph","content":[{"nodeType":"text","value":"Terraform makes this seeming simple: ","marks":[],"data":{}}],"data":{}},{"nodeType":"ordered-list","content":[{"nodeType":"list-item","content":[{"nodeType":"paragraph","content":[{"nodeType":"text","value":"","marks":[],"data":{}},{"nodeType":"hyperlink","content":[{"nodeType":"text","value":"Remove the user from teammembers.auto.tfvars","marks":[],"data":{}}],"data":{"uri":"https://github.com/sparkbox/aws-infrastructure/commit/6ace68ae4fce1f76e45769c3de6d5a8fbd8f9a71#diff-4b9b83568bdde9f27a3f6763676955c22280a5a7bb9fde8a1ca9e17212b73506"}},{"nodeType":"text","value":". ","marks":[],"data":{}}],"data":{}}],"data":{}},{"nodeType":"list-item","content":[{"nodeType":"paragraph","content":[{"nodeType":"text","value":"Push the commit to Github","marks":[],"data":{}}],"data":{}}],"data":{}},{"nodeType":"list-item","content":[{"nodeType":"paragraph","content":[{"nodeType":"text","value":"","marks":[],"data":{}},{"nodeType":"hyperlink","content":[{"nodeType":"text","value":"Terraform Cloud","marks":[],"data":{}}],"data":{"uri":"https://www.terraform.io/cloud"}},{"nodeType":"text","value":" kicks off a new run, planing and auto applying the changes necessary","marks":[],"data":{}}],"data":{}}],"data":{}}],"data":{}},{"nodeType":"paragraph","content":[{"nodeType":"text","value":"Simple! User, policies, login profile, etc are all destroyed.","marks":[],"data":{}}],"data":{}},{"nodeType":"paragraph","content":[{"nodeType":"text","value":"","marks":[],"data":{}}],"data":{}},{"nodeType":"paragraph","content":[{"nodeType":"text","value":"Remove Access to Sandbox Accounts","marks":[{"type":"bold"}],"data":{}}],"data":{}},{"nodeType":"paragraph","content":[{"nodeType":"text","value":"Unfortunately, ","marks":[],"data":{}},{"nodeType":"hyperlink","content":[{"nodeType":"text","value":"consolidated billing","marks":[],"data":{}}],"data":{"uri":"https://docs.aws.amazon.com/awsaccountbilling/latest/aboutv2/consolidated-billing.html"}},{"nodeType":"text","value":" throws a wrench in the situation. Deleting an account really just removes it from the organization. To do this, the account must have a payment method configured. This isn't an option to automate.","marks":[],"data":{}}],"data":{}},{"nodeType":"paragraph","content":[{"nodeType":"text","value":"Our goal is to remove former team members' access to Sparkbox resources. Our Terraform automation was conflating this with deleting their accounts. It's reasonable think that, but not necessary. So instead, we'll separate the concepts of ","marks":[],"data":{}},{"nodeType":"text","value":"enabled users that should have access","marks":[{"type":"italic"}],"data":{}},{"nodeType":"text","value":" from ","marks":[],"data":{}},{"nodeType":"text","value":"all users that we have created accounts","marks":[{"type":"italic"}],"data":{}},{"nodeType":"text","value":" in our Terraform code:","marks":[],"data":{}}],"data":{}},{"nodeType":"paragraph","content":[{"nodeType":"text","value":"locals {\n  enabled_user_names = [\n    for user in var.users : user.name\n    if user.is_enabled\n  ]","marks":[{"type":"code"}],"data":{}}],"data":{}},{"nodeType":"paragraph","content":[{"nodeType":"text","value":"  all_user_names = toset([\n    for user in var.users : user.name\n  ])\n}","marks":[{"type":"code"}],"data":{}}],"data":{}},{"nodeType":"paragraph","content":[{"nodeType":"text","value":"Now we can use these individually local variables for their unique purposes:","marks":[],"data":{}}],"data":{}},{"nodeType":"paragraph","content":[{"nodeType":"text","value":"","marks":[{"type":"italic"}],"data":{}},{"nodeType":"hyperlink","content":[{"nodeType":"text","value":"Users that should exist:","marks":[{"type":"italic"}],"data":{}}],"data":{"uri":"https://github.com/sparkbox/aws-infrastructure/blob/24e4796315876292b4562bd2aa39efc6d85771a7/modules/development-team/main.tf#L46-L50"}},{"nodeType":"text","value":"","marks":[{"type":"italic"}],"data":{}}],"data":{}},{"nodeType":"paragraph","content":[{"nodeType":"text","value":"resource \"aws_iam_user\" \"user\" {\n  for_each = toset(local.enabled_user_names)","marks":[{"type":"code"}],"data":{}}],"data":{}},{"nodeType":"paragraph","content":[{"nodeType":"text","value":"  name = each.value\n}","marks":[{"type":"code"}],"data":{}}],"data":{}},{"nodeType":"paragraph","content":[{"nodeType":"text","value":"","marks":[{"type":"italic"}],"data":{}},{"nodeType":"hyperlink","content":[{"nodeType":"text","value":"Accounts that should exist:","marks":[{"type":"italic"}],"data":{}}],"data":{"uri":"https://github.com/sparkbox/aws-infrastructure/blob/24e4796315876292b4562bd2aa39efc6d85771a7/modules/development-team/main.tf#L59-L66"}},{"nodeType":"text","value":"","marks":[{"type":"italic"}],"data":{}}],"data":{}},{"nodeType":"paragraph","content":[{"nodeType":"text","value":"resource \"aws_organizations_account\" \"account\" {\n  for_each  = local.all_user_names","marks":[{"type":"code"}],"data":{}}],"data":{}},{"nodeType":"paragraph","content":[{"nodeType":"text","value":"  name      = \"Dev Sandbox ${each.value}\"\n  email     = \"${each.value}@heysparkbox.com\"\n  role_name = \"Administrator\"\n  parent_id = aws_organizations_organizational_unit.dev_team_ou.id\n}","marks":[{"type":"code"}],"data":{}}],"data":{}},{"nodeType":"embedded-asset-block","content":[],"data":{"target":{"sys":{"id":"1BPX0t7CimxM9AetetXujM","type":"Link","linkType":"Asset"}}}},{"nodeType":"paragraph","content":[{"nodeType":"text","value":"","marks":[],"data":{}}],"data":{}},{"nodeType":"paragraph","content":[{"nodeType":"text","value":"With this setup, former Sparkboxers no longer have accounts, login profiles, or any access to Sparkbox resources. We can still gain access to their sub account for whatever purpose we might have. And, if we choose, ","marks":[],"data":{}},{"nodeType":"hyperlink","content":[{"nodeType":"text","value":"remove and delete the member account","marks":[],"data":{}}],"data":{"uri":"https://docs.aws.amazon.com/organizations/latest/userguide/orgs_manage_accounts_remove.html"}},{"nodeType":"text","value":".","marks":[],"data":{}}],"data":{}}]}}},{"metadata":{"tags":[],"concepts":[]},"sys":{"space":{"sys":{"type":"Link","linkType":"Space","id":"w95trmn9jf9t"}},"id":"4YV3qvLTEm0WXYvi5hSVRo","type":"Entry","createdAt":"2021-06-23T03:13:02.688Z","updatedAt":"2021-07-16T16:12:02.836Z","environment":{"sys":{"id":"master","type":"Link","linkType":"Environment"}},"publishedVersion":19,"revision":4,"contentType":{"sys":{"type":"Link","linkType":"ContentType","id":"externalContent"}},"locale":"en-US"},"fields":{"title":"Traits of a Build and Deployment Pipeline","abstract":{"nodeType":"document","data":{},"content":[{"nodeType":"paragraph","content":[{"nodeType":"text","value":"I believe that a team's release cadence has an incredible impact on their success and enjoyment. I wrote a little about the traits that make up a successful build and deploy pipeline for our team at Sparkbox.","marks":[],"data":{}}],"data":{}}]},"url":"https://sparkbox.com/foundry/successful_software_build_and_deployment_pipelines"}},{"metadata":{"tags":[],"concepts":[]},"sys":{"space":{"sys":{"type":"Link","linkType":"Space","id":"w95trmn9jf9t"}},"id":"1oUbvCK6ErsO3sewMN9GUC","type":"Entry","createdAt":"2021-06-23T03:26:46.514Z","updatedAt":"2021-07-16T16:11:56.628Z","environment":{"sys":{"id":"master","type":"Link","linkType":"Environment"}},"publishedVersion":33,"revision":4,"contentType":{"sys":{"type":"Link","linkType":"ContentType","id":"externalContent"}},"locale":"en-US"},"fields":{"title":"Making the Leap to Tech Lead","abstract":{"nodeType":"document","data":{},"content":[{"nodeType":"paragraph","content":[{"nodeType":"text","value":"Building a healthy, sustainable development organization involves defining technical leadership. In 2019, I shared my thoughts on the tech lead role at the Solidus conference in Utah. Shortly after, I hiked City Creek Canyon and fell in love with the area.","marks":[],"data":{}}],"data":{}}]},"url":"https://youtu.be/Lx8BepRpfvg"}}]}},"page":"/","query":{"feed":"rss2"},"buildId":"rDuBolMZiJEQRR-qkhWFX","isFallback":false,"gip":true}</script><script nomodule="" src="/_next/static/chunks/polyfills-eef578260fd80f8fff94.js"></script><script src="/_next/static/chunks/webpack-189c53927ffd3caf09c3.js" async=""></script><script src="/_next/static/chunks/framework-92300432a1172ef1338b.js" async=""></script><script src="/_next/static/chunks/main-71948af4b0f09c0fc30e.js" async=""></script><script src="/_next/static/chunks/pages/_app-44a7fcb1aeab50f8aaeb.js" async=""></script><script src="/_next/static/chunks/203-806adfe910971c6684ee.js" async=""></script><script src="/_next/static/chunks/pages/index-1a1a0750ec0c7ead8ae3.js" async=""></script><script src="/_next/static/rDuBolMZiJEQRR-qkhWFX/_buildManifest.js" async=""></script><script src="/_next/static/rDuBolMZiJEQRR-qkhWFX/_ssgManifest.js" async=""></script></body></html>