{"id":14871,"date":"2019-12-05T18:04:45","date_gmt":"2019-12-05T18:04:45","guid":{"rendered":"https:\/\/ushipblogsubd.wpengine.com\/?p=14871"},"modified":"2025-09-03T15:07:18","modified_gmt":"2025-09-03T15:07:18","slug":"effortless-ami-deployments-with-chef-infra-and-habitat-part-1","status":"publish","type":"post","link":"https:\/\/ushipblogsubd.wpengine.com\/company-news\/effortless-ami-deployments-with-chef-infra-and-habitat-part-1\/","title":{"rendered":"Effortless AMI Deployments with Chef Infra and Habitat &#8211; Part 1"},"content":{"rendered":"<p>Background<\/p>\n<p>At uShip, we&#8217;ve been moving to an AMI deployment strategy for standing up web servers that houses our main application. We made the decision as part of a larger strategy to ensure our environments (dev, qa, prod, etc.) were as similar as possible. We figured that if we could build a single AMI that is deployed to every environment, that would be a huge step in accomplishing environment parity. While the process has mostly been straight-forward, we have run into a problem and the new\u00a0Effortless Infrastructure Pattern\u00a0from Chef provided an elegant solution.<\/p>\n<p>The Problem<\/p>\n<p>Chef Infra\u00a0is a great way of managing configuration for servers. One of the biggest reasons that we reached for Chef versus something else is the Windows support. While other options have gotten better, Chef had the support back in 2015 when we were evaluating configuration management solutions. Chef&#8217;s client\/server model allowed us to get visibilty into our fleet. However, that visibility comes at a cost.<\/p>\n<p>The cost has to do with bootstrapping nodes into the Chef Server. Traditionally, this process works well as you&#8217;d usually have long-lived nodes and if you wanted to remove one, you could do that manually using the\u00a0chef-server-ctl. With our AMI deployment strategy, we were creating and destroying nodes every deployment so we were left with many missing nodes and no easy way of cleaning them up. Before we get into the effortless pattern, let&#8217;s look at the traditional way of bootstrapping a node.<\/p>\n<p>Bootstrapping Chef Nodes<\/p>\n<p>In Chef, bootstrapping is the process that installs the Chef Infra Client and sets up the node to communicate with the Chef Server. This can either be done using the\u00a0knife bootstrap\u00a0command from your workstation or, in the case of AWS, with a user data script. Here&#8217;s an example of what we were using for an unattended bootstrap:<\/p>\n<p>Write-Output &#8220;Pull the encrypted_data_bag_secret key from S3&#8221;<br \/>\n&amp;amp; &#8220;C:\/Program Files\/Amazon\/AWSCLI\/bin\/aws.exe&#8221; s3 cp s3:\/\/&amp;lt;my-super-real-s3-bucket&amp;gt;\/default-validator.pem C:\/chef\/<br \/>\n&amp;amp; &#8220;C:\/Program Files\/Amazon\/AWSCLI\/bin\/aws.exe&#8221; s3 cp s3:\/\/&amp;lt;my-super-real-s3-bucket&amp;gt;\/encrypted_data_bag_secret C:\/chef\/encrypted_data_bag_secret<\/p>\n<p>Write-Output &#8220;Create first-boot.json for Chef bootstrap into $environment policy_group&#8221;<br \/>\n$firstBoot = @{&#8220;policy_name&#8221; = &#8220;web&#8221;; &#8220;policy_group&#8221; = &#8220;$environment&#8221; }<br \/>\nSet-Content -Path C:\/chef\/first-boot.json -Value ($firstboot | ConvertTo-Json -Depth 10)<\/p>\n<p>Write-Output &#8220;Create client.rb file for Chef using a dynamically-generated node name&#8221;<br \/>\n$nodeName = &#8220;$(hostname)-{0}&#8221; -f ( -join ((65..90) + (97..122) | Get-Random -Count 4 | % { [char]$_ }))<\/p>\n<p>$clientrb = @&#8221;<br \/>\n chef_server_url &#8216;https:\/\/chef-server.example.com\/organizations\/default&#8217;<br \/>\n validation_client_name &#8216;default-validator&#8217;<br \/>\n validation_key &#8216;C:\/chef\/default-validator.pem&#8217;<br \/>\n node_name &#8216;{0}&#8217;<br \/>\n&#8220;@ -f $nodeName<br \/>\nSet-Content -Path C:\/chef\/client.rb -Value $clientrb<\/p>\n<p>Write-Output &#8220;Run Chef client first time&#8221;<br \/>\nC:\/opscode\/chef\/bin\/chef-client.bat -j C:\/chef\/first-boot.json<\/p>\n<p>&amp;nbsp;<\/p>\n<p>I&#8217;d like to note that we were originally using\u00a0Chef Vault\u00a0to store secrets but there doesn&#8217;t appear to be a way for a node to bootstrap itself and then give itself permissions to a vault item and so we&#8217;re using\u00a0encrypted data bags\u00a0here.<\/p>\n<p>Assuming that you&#8217;ve set up your S3 bucket policy and EC2 instance role, this solution works well to bring up instances. But, as mentioned earlier, if you boot up four new servers in each environment every time you deploy, you&#8217;ll have an increasing number of missing nodes. There is a\u00a0Lambda\u00a0out on the interwebs for cleaning up nodes in the Chef Server, but this is kinda of a pain to do and only addresses the Chef Server; it does nothing for the ones in\u00a0Chef Automate.<\/p>\n<p>Effortless Infrastructure<\/p>\n<p>If you missed the session from ChefConf 2019, there&#8217;s an excellent talk by David Echols about\u00a0what effortless config is. Essentially, the effortless pattern is a way to build and run your cookbooks as a single, deployable package. It accomplishes this using\u00a0Habitat,\u00a0Policyfiles, and\u00a0Chef Solo. Before reading further, I urge you to check out that video and the track on\u00a0Learn Chef Rally.<\/p>\n<p>Prerequisites<\/p>\n<p>Chef Workstation<br \/>\nHabitat<\/p>\n<p>Generate a Cookbook<\/p>\n<p>The first thing we need to do is generate a new cookbook. I&#8217;m going to deploy a cookbook that sets up IIS on a Windows server but the concepts should be similar if you&#8217;re deploying Linux servers.<\/p>\n<p>PS C:\\Users\\uship\\Projects&amp;gt; chef generate cookbook webserver<br \/>\nGenerating cookbook webserver<br \/>\n&#8211; Ensuring correct cookbook content<br \/>\n&#8211; Committing cookbook files to git<\/p>\n<p>Your cookbook is ready. To setup the pipeline, type `cd webserver`, then run `delivery init`<\/p>\n<p>&amp;nbsp;<\/p>\n<p>Let&#8217;s check out the content of the\u00a0webserver\u00a0cookbook:<\/p>\n<p>PS C:\\Users\\uship\\Projects&amp;gt; cd webserver<br \/>\nPS C:\\Users\\uship\\Projects\\webserver&amp;gt; tree<br \/>\n.<br \/>\n\u251c\u2500\u2500 CHANGELOG.md<br \/>\n\u251c\u2500\u2500 LICENSE<br \/>\n\u251c\u2500\u2500 Policyfile.rb<br \/>\n\u251c\u2500\u2500 README.md<br \/>\n\u251c\u2500\u2500 chefignore<br \/>\n\u251c\u2500\u2500 kitchen.yml<br \/>\n\u251c\u2500\u2500 metadata.rb<br \/>\n\u251c\u2500\u2500 recipes<br \/>\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 default.rb<br \/>\n\u251c\u2500\u2500 spec<br \/>\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 spec_helper.rb<br \/>\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 unit<br \/>\n\u2502\u00a0\u00a0     \u2514\u2500\u2500 recipes<br \/>\n\u2502\u00a0\u00a0         \u2514\u2500\u2500 default_spec.rb<br \/>\n\u2514\u2500\u2500 test<br \/>\n    \u2514\u2500\u2500 integration<br \/>\n        \u2514\u2500\u2500 default<br \/>\n            \u2514\u2500\u2500 default_test.rb<\/p>\n<p>7 directories, 11 files<\/p>\n<p>&amp;nbsp;<\/p>\n<p>To set up IIS, we&#8217;re going to leverage the\u00a0iis cookbook. Add the following to the\u00a0metadata.rb\u00a0file:<\/p>\n<p>name &#8216;webserver&#8217;<br \/>\nmaintainer &#8216;The Authors&#8217;<br \/>\n.<br \/>\n.<br \/>\n.<br \/>\n# source_url &#8216;https:\/\/github.com\/&amp;lt;insert_org_here&amp;gt;\/webserver&#8217;<\/p>\n<p>depends &#8216;iis&#8217;, &#8216;~&amp;gt; 7.2.0&#8217;<\/p>\n<p>&amp;nbsp;<\/p>\n<p>We&#8217;ll need to go ahead and install the dependencies. For this, we&#8217;ll leverage Policyfiles. If you are unfamiliar, they&#8217;re basically what replaces\u00a0Berkshelf\u00a0and environments\/roles. Check out the\u00a0documentation\u00a0but you should just need to run the following:<\/p>\n<p>PS C:\\Users\\uship\\Projects\\webserver&amp;gt; chef install<br \/>\nBuilding policy webserver<br \/>\nExpanded run list: recipe[webserver::default]<br \/>\nCaching Cookbooks&#8230;<br \/>\nInstalling webserver &amp;gt;= 0.0.0 from path<br \/>\nInstalling iis       7.2.0<br \/>\nInstalling windows   6.0.1<\/p>\n<p>Lockfile written to \/Users\/uship\/Documents\/effortless_ami_deployments\/webserver\/Policyfile.lock.json<br \/>\nPolicy revision id: c2746cac28e13e1dae4fa99f4b9f9d56e5b7bf11894f1cce1e8940a2f4de42c3<\/p>\n<p>&amp;nbsp;<\/p>\n<p>Now that we have our dependencies installed, let&#8217;s update the Chef recipe to install IIS.<\/p>\n<p>#<br \/>\n# Cookbook:: webserver<br \/>\n# Recipe:: default<br \/>\n#<br \/>\n# Copyright:: 2019, The Authors, All Rights Reserved.<\/p>\n<p>include_recipe &#8216;iis&#8217;<\/p>\n<p>&amp;nbsp;<\/p>\n<p>This will install IIS on the server and enable the W3SVC service. At this point, if you boot up a\u00a0Test Kitchen\u00a0instance to test and then browse to the IP address, you should see the default Internet Information Services page.<\/p>\n<p>Package the Cookbook<\/p>\n<p>As I said earlier, the effortless infrastructure pattern leverages\u00a0Habitat\u00a0to package and run your Chef cookbook like an application. To package this up, we&#8217;ll need to habitatize our application and create a basic structure. Note that this is going to be deployed and run on a Windows server so it needs to be built on a Windows box to work properly. If you&#8217;re working on Mac or Linux, the concepts are the same but you&#8217;d use Bash instead of Powershell for writing your plan. Again, I&#8217;ll defer to the\u00a0Habitat documentation\u00a0for the specifics.<\/p>\n<p>From the root of your cookbook directory, initialize the Habitat plan, using your\u00a0origin:<\/p>\n<p>PS C:\\Users\\uship\\Projects\\webserver&amp;gt; hab plan init -o uship<br \/>\n\u00bb Constructing a cozy habitat for your app&#8230;<\/p>\n<p>\u03a9 Creating file: habitat\/plan.ps1<br \/>\n  `plan.sh` is the foundation of your new habitat. It contains metadata,<br \/>\n  dependencies, and tasks.<\/p>\n<p>\u03a9 Creating file: habitat\/default.toml<br \/>\n  `default.toml` contains default values for `cfg` prefixed variables.<\/p>\n<p>\u03a9 Creating file: habitat\/README.md<br \/>\n  `README.md` contains a basic README document which you should update.<\/p>\n<p>\u03a9 Creating directory: habitat\/config\/<br \/>\n  `\/config\/` contains configuration files for your app.<\/p>\n<p>\u03a9 Creating directory: habitat\/hooks\/<br \/>\n  `\/hooks\/` contains automation hooks into your habitat.<\/p>\n<p>  For more information on any of the files:<br \/>\n  https:\/\/www.habitat.sh\/docs\/reference\/plan-syntax\/<\/p>\n<p>\u2192 Using existing file: habitat\/..\/.gitignore (1 lines appended)<br \/>\n\u2261 An abode for your code is initialized!<\/p>\n<p>&amp;nbsp;<\/p>\n<p>For the effortless infrastructure, we&#8217;ll lean on the\u00a0Habitat Scaffolding\u00a0provided by the Habitat core team. You can see what the scaffolding is doing by looking in the\u00a0repository, but all we need to do is update the\u00a0habitat\/plan.ps1\u00a0file:<\/p>\n<p># This is the name of our Habitat package<br \/>\n$pkg_name=&#8221;webserver&#8221;<\/p>\n<p># Update this with your origin<br \/>\n$pkg_origin=&#8221;uship&#8221;<\/p>\n<p># Package version. Typically follomws Semantic Versioning<br \/>\n$pkg_version=&#8221;0.0.1&#8243;<\/p>\n<p># Update this per your preferences<br \/>\n$pkg_maintainer=&#8221;uShip, Inc. &amp;lt;devops@uship.com&amp;gt;&#8221;<\/p>\n<p># We need these dependencies for our application to run<br \/>\n$pkg_deps=@(<br \/>\n  &#8220;core\/cacerts&#8221;<br \/>\n  &#8220;stuartpreston\/chef-client&#8221; # https:\/\/github.com\/habitat-sh\/habitat\/issues\/6671<br \/>\n)<\/p>\n<p># Use the scaffolding-chef-infra scaffolding<br \/>\n$pkg_scaffolding=&#8221;chef\/scaffolding-chef-infra&#8221;<\/p>\n<p># Name of our Policyfile<br \/>\n$scaffold_policy_name=&#8221;Policyfile&#8221;<\/p>\n<p># Location of the Policyfile. In this case, habitat\/..\/Policyfile.rb<br \/>\n$scaffold_policyfile_path=&#8221;$PLAN_CONTEXT\/..\/&#8221;<\/p>\n<p>&amp;nbsp;<\/p>\n<p>The last thing we need to do before we can build our Habitat package is update the configuration for the Chef Client that will be running. Habitat uses\u00a0Toml\u00a0for configuration and the default config is in\u00a0habitat\/default.toml:<\/p>\n<p># Use this file to templatize your application&#8217;s native configuration files.<br \/>\n# See the docs at https:\/\/www.habitat.sh\/docs\/create-packages-configure\/.<br \/>\n# You can safely delete this file if you don&#8217;t need it.<\/p>\n<p># Run the Chef Client every 5 minutes<br \/>\ninterval = 300<\/p>\n<p># Offset the Chef Client runs by 30 seconds<br \/>\nsplay = 30<\/p>\n<p># No offset for the first run<br \/>\nsplay_first_run = 0<\/p>\n<p># Wait for Chef Client run lock file to be deleted<br \/>\nrun_lock_timeout = 300<\/p>\n<p>&amp;nbsp;<\/p>\n<p>Go ahead and remove the\u00a0habitat\/config\u00a0and\u00a0habitat\/hooks\u00a0directories as these aren&#8217;t needed and tend to cause errors with the build:<\/p>\n<p>PS C:\\Users\\uship\\Projects\\webserver&amp;gt; rmdir habitat\/config<br \/>\nPS C:\\Users\\uship\\Projects\\webserver&amp;gt; rmdir habitat\/hooks<\/p>\n<p>&amp;nbsp;<\/p>\n<p>To build our Habitat package, we&#8217;ll enter the Habitat studio. The studio is a clean room which only packages up the dependencies that have been specified and nothing else.<\/p>\n<p>PS C:\\Users\\uship\\Projects\\webserver&amp;gt; hab studio enter<br \/>\nWARNING: Using a local Studio. To use a Docker studio, use the -D argument.<br \/>\n   hab-studio: Creating Studio at C:\\hab\\studios\\Users&#8211;uship&#8211;Projects&#8211;webserver<br \/>\n\u00bb Importing origin key from standard input<br \/>\n\u2261 Imported public origin key uship-20190919164651.<br \/>\n\u00bb Importing origin key from standard input<br \/>\n\u2261 Imported secret origin key uship-20190919164651.<br \/>\n** The Habitat Supervisor has been started in the background.<br \/>\n** Use &#8216;hab svc start&#8217; and &#8216;hab svc stop&#8217; to start and stop services.<br \/>\n** Use the &#8216;Get-SupervisorLog&#8217; command to stream the Supervisor log.<br \/>\n** Use the &#8216;Stop-Supervisor&#8217; to terminate the Supervisor.<\/p>\n<p>   hab-studio: Entering Studio at C:\\hab\\studios\\Users&#8211;uship&#8211;Projects&#8211;webserver<br \/>\n[HAB-STUDIO] Habitat:\\src&amp;gt;<\/p>\n<p>&amp;nbsp;<\/p>\n<p>Inside the studio, we&#8217;ll run\u00a0build\u00a0which will use the default location of the plan file in\u00a0habitat\/plan.ps1:<\/p>\n<p>[HAB-STUDIO] Habitat:\\src&amp;gt; build<br \/>\n   : Loading C:\\hab\\studios\\Users&#8211;uship&#8211;Projects&#8211;webserver\\src\\habitat\\plan.ps1<br \/>\n   webserver: Plan loaded<br \/>\n   webserver: Validating plan metadata<br \/>\n   webserver: hab-plan-build.ps1 setup<br \/>\n   webserver: Using HAB_BIN=C:\\hab\\pkgs\\core\\hab-studio\\0.83.0\\20190712234514\\bin\\hab\\hab.exe for installs, signing, and hashing<br \/>\n   webserver: Resolving scaffolding dependencies<br \/>\n\u00bb Installing chef\/scaffolding-chef-infra<br \/>\n\u2302 Determining latest version of chef\/scaffolding-chef-infra in the &#8216;stable&#8217; channel<br \/>\n\u2192 Using chef\/scaffolding-chef-infra\/0.16.0\/20191028151207<br \/>\n\u2261 Install of chef\/scaffolding-chef-infra\/0.16.0\/20191028151207 complete with 0 new packages installed.<br \/>\n   webserver: Resolved scaffolding dependency &#8216;chef\/scaffolding-chef-infra&#8217; to C:\\hab\\studios\\Users&#8211;uship&#8211;Projects&#8211;webserver\\hab\\pkgs\\chef\\scaffolding-chef-infra\\0.16.0\\20191028151207<br \/>\n   webserver: Loading Scaffolding C:\\hab\\studios\\Users&#8211;uship&#8211;Projects&#8211;webserver\\hab\\pkgs\\chef\\scaffolding-chef-infra\\0.16.0\\20191028151207\/lib\/scaffolding.ps1<br \/>\n\u00bb Installing chef\/scaffolding-chef-infra<br \/>\n\u2302 Determining latest version of chef\/scaffolding-chef-infra in the &#8216;stable&#8217; channel<br \/>\n\u2192 Using chef\/scaffolding-chef-infra\/0.16.0\/20191028151207<br \/>\n\u2261 Install of chef\/scaffolding-chef-infra\/0.16.0\/20191028151207 complete with 0 new packages installed.<br \/>\n   webserver: Resolved build dependency &#8216;chef\/scaffolding-chef-infra&#8217; to C:\\hab\\studios\\Users&#8211;uship&#8211;Projects&#8211;webserver\\hab\\pkgs\\chef\\scaffolding-chef-infra\\0.16.0\\20191028151207<br \/>\n\u00bb Installing core\/chef-dk\/2.5.3\/20180416182816<br \/>\n\u2192 Using core\/chef-dk\/2.5.3\/20180416182816<br \/>\n.<br \/>\n.<br \/>\n.<br \/>\n   webserver: Preparing to build<br \/>\n   webserver: Building<br \/>\nBuilding policy webserver<br \/>\nExpanded run list: recipe[webserver::default]<br \/>\nCaching Cookbooks&#8230;<br \/>\nInstalling webserver &amp;gt;= 0.0.0 from path<br \/>\nUsing      iis       7.2.0<br \/>\nUsing      windows   6.0.1<\/p>\n<p>Lockfile written to C:\/hab\/studios\/Users&#8211;uship&#8211;Projects&#8211;webserver\/src\/Policyfile.lock.json<br \/>\nPolicy revision id: f8a3f2d55e079328c164d2c0250854348cdb7900e89c4c8e9cbe155825d7635b<br \/>\n   webserver: Installing<br \/>\nExported policy &#8216;webserver&#8217; to C:\\hab\\studios\\Users&#8211;uship&#8211;Projects&#8211;webserver\\hab\\pkgs\\uship\\webserver\\0.0.1\\20191114064617<\/p>\n<p>To converge this system with the exported policy, run:<br \/>\n  cd C:\\hab\\studios\\Users&#8211;uship&#8211;Projects&#8211;webserver\\hab\\pkgs\\uship\\webserver\\0.0.1\\20191114064617<br \/>\n  chef-client -z<\/p>\n<p>    Directory: C:\\hab\\studios\\Users&#8211;uship&#8211;Projects&#8211;webserver\\hab\\pkgs\\uship\\webserver\\0.0.1\\20191114064617<\/p>\n<p>Mode                LastWriteTime         Length Name<br \/>\n&#8212;-                &#8212;&#8212;&#8212;&#8212;-         &#8212;&#8212; &#8212;-<br \/>\nd&#8212;&#8211;        11\/14\/2019  6:47 AM                config<br \/>\n   webserver: Writing configuration<br \/>\n   webserver: Writing default.toml<br \/>\nd&#8212;&#8211;        11\/14\/2019  6:47 AM                hooks<br \/>\n   webserver: Creating manifest<br \/>\n   webserver: Building package metadata<br \/>\n   webserver: Generating package artifact<br \/>\n\u00bb Signing C:\\hab\\studios\\Users&#8211;uship&#8211;Projects&#8211;webserver\\hab\\cache\\artifacts\\.uship-webserver-0.0.1-20191114064617-x86_64-windows.tar.xz<br \/>\n\u2192 Signing C:\\hab\\studios\\Users&#8211;uship&#8211;Projects&#8211;webserver\\hab\\cache\\artifacts\\.uship-webserver-0.0.1-20191114064617-x86_64-windows.tar.xz with uship-20190919164651 to create C:\\hab\\studios\\Users&#8211;uship&#8211;Projects&#8211;webserver\\hab\\cache\\artifacts\\uship-webserver-0.0.1-20191114064617-x86_64-windows.hart<br \/>\n\u2261 Signed artifact C:\\hab\\studios\\Users&#8211;uship&#8211;Projects&#8211;webserver\\hab\\cache\\artifacts\\uship-webserver-0.0.1-20191114064617-x86_64-windows.hart.<br \/>\n   webserver: hab-plan-build.ps1 cleanup<br \/>\n   webserver:<br \/>\n   webserver: Source Cache: C:\\hab\\studios\\Users&#8211;uship&#8211;Projects&#8211;webserver\\hab\\cache\\src\\webserver-0.0.1<br \/>\n   webserver: Installed Path: C:\\hab\\studios\\Users&#8211;uship&#8211;Projects&#8211;webserver\\hab\\pkgs\\uship\\webserver\\0.0.1\\20191114064617<br \/>\n   webserver: Artifact: C:\\hab\\studios\\Users&#8211;uship&#8211;Projects&#8211;webserver\\src\\results\\uship-webserver-0.0.1-20191114064617-x86_64-windows.hart<br \/>\n   webserver: Build Report: C:\\hab\\studios\\Users&#8211;uship&#8211;Projects&#8211;webserver\\src\\results\\last_build.ps1<br \/>\n   webserver: SHA256 Checksum:<br \/>\n   webserver: Blake2b Checksum:<br \/>\n   webserver:<br \/>\n   webserver: I love it when a plan.ps1 comes together.<br \/>\n   webserver:<\/p>\n<p>&amp;nbsp;<\/p>\n<p>If everything is successful, the newly-built package will be in the\u00a0results\u00a0directory. Let&#8217;s go ahead and push it to the\u00a0Habitat Bldr Service. We can use the\u00a0results\/last_build.ps1\u00a0file to set variables so we don&#8217;t need to specify the full path to the artifact. Note that you&#8217;ll need to make sure your\u00a0auth token\u00a0is set up.<\/p>\n<p>PS C:\\Users\\uship\\Projects\\webserver\\results&amp;gt; . .\\last_build.ps1<br \/>\nPS C:\\Users\\uship\\Projects\\webserver\\results&amp;gt; hab pkg upload $pkg_artifact<br \/>\n    79 B \/ 79 B | [=====================================================================================================================================================================================] 100.00 % 654 B\/s<br \/>\n\u2192 Using existing public origin key uship-20190919164651.pub<br \/>\n\u2192 Using existing core\/cacerts\/2019.08.28\/20190829172945<br \/>\n\u2192 Using existing stuartpreston\/chef-client\/14.11.21\/20190328012639<br \/>\n\u2191 Uploading uship-webserver-0.0.1-20191114064617-x86_64-windows.hart<br \/>\n    70.89 KB \/ 70.89 KB | [===========================================================================================================================================================================] 100.00 % 1.45 MB\/s<br \/>\n\u221a Uploaded uship\/webserver\/0.0.1\/20191114064617<br \/>\n\u2261 Upload of uship\/webserver\/0.0.1\/20191114064617 complete.<\/p>\n<p>&amp;nbsp;<\/p>\n<p>You should now have a public &#8220;webserver&#8221; package available in the &#8220;unstable&#8221; channel of your Habitat origin. In the next part of this blog post series, we&#8217;ll build an AMI and deploy our new package to a server using that AMI. If you want to see the code for this, it&#8217;s available at\u00a0https:\/\/github.com\/uShip\/effortless_ami_deployments\u00a0and the Habitat package is at\u00a0https:\/\/bldr.habitat.sh\/#\/pkgs\/uship\/webserver.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Background At uShip, we&#8217;ve been moving to an AMI deployment strategy for standing up web servers that houses our main application. We made the decision as part of a larger strategy to ensure our environments (dev, qa, prod, etc.) were as similar as possible. We figured that if we could build a single AMI that&#8230;<a class=\"read-more\" href=\"https:\/\/ushipblogsubd.wpengine.com\/company-news\/effortless-ami-deployments-with-chef-infra-and-habitat-part-1\/\"> Read More<\/a><\/p>\n","protected":false},"author":51,"featured_media":14947,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[2],"tags":[297],"class_list":["post-14871","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-company-news","tag-shipping-code"],"acf":{"blog_post_content":null},"_links":{"self":[{"href":"https:\/\/ushipblogsubd.wpengine.com\/wp-json\/wp\/v2\/posts\/14871","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/ushipblogsubd.wpengine.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/ushipblogsubd.wpengine.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/ushipblogsubd.wpengine.com\/wp-json\/wp\/v2\/users\/51"}],"replies":[{"embeddable":true,"href":"https:\/\/ushipblogsubd.wpengine.com\/wp-json\/wp\/v2\/comments?post=14871"}],"version-history":[{"count":0,"href":"https:\/\/ushipblogsubd.wpengine.com\/wp-json\/wp\/v2\/posts\/14871\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/ushipblogsubd.wpengine.com\/wp-json\/wp\/v2\/media\/14947"}],"wp:attachment":[{"href":"https:\/\/ushipblogsubd.wpengine.com\/wp-json\/wp\/v2\/media?parent=14871"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/ushipblogsubd.wpengine.com\/wp-json\/wp\/v2\/categories?post=14871"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/ushipblogsubd.wpengine.com\/wp-json\/wp\/v2\/tags?post=14871"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}