{"id":452,"date":"2026-10-14T12:01:22","date_gmt":"2026-10-14T12:01:22","guid":{"rendered":"http:\/\/www.scmgalaxy.com\/tutorials\/2015\/10\/27\/provision-a-aws-ec2-vm-using-chef\/"},"modified":"2026-10-14T12:01:22","modified_gmt":"2026-10-14T12:01:22","slug":"provision-a-aws-ec2-vm-using-chef","status":"publish","type":"post","link":"https:\/\/www.devopsschool.com\/blog\/provision-a-aws-ec2-vm-using-chef\/","title":{"rendered":"Provision an AWS EC2 Instance with Chef (Legacy ChefDK Workflow)"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\"><em>Originally published on 2015-10-27; fully revised and updated.<\/em><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This guide explains how to provision an AWS EC2 instance with the ChefDK-era <strong>chef-provisioning<\/strong> workflow. It is intended for teams maintaining older Chef automation and covers prerequisites, AWS credential setup, cookbook and Policyfile creation, and running <code>chef provision<\/code> safely.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Prerequisites and legacy context<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The original process is based on older Chef tooling and patterns. Keep that in mind if your environment has moved to newer Chef workflows.<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>Install ChefDK (original prerequisite link): <a href=\"tutorials\/how-to-install-chefdk-in-rhel-ubantu-mac-and-windows\" target=\"_blank\" rel=\"noopener noreferrer\">tutorials\/how-to-install-chefdk-in-rhel-ubantu-mac-and-windows<\/a><\/li><li>Set up AWS credentials (original prerequisite link): <a href=\"tutorials\/setup-aws-credentails-using-aws-command-line-interface\" target=\"_blank\" rel=\"noopener noreferrer\">tutorials\/setup-aws-credentails-using-aws-command-line-interface<\/a><\/li><li>Configure Knife (original prerequisite link): <a href=\"tutorials\/configure-the-knife-command\" target=\"_blank\" rel=\"noopener noreferrer\">tutorials\/configure-the-knife-command<\/a><\/li><li>An AWS account with EC2 permissions, a key pair in your target region, and network access for SSH\/bootstrap.<\/li><\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Provisioning workflow<\/h2>\n\n\n\n<pre class=\"mermaid\">\nflowchart TD\n    A[Install and configure Chef tooling] --> B[Export AWS credentials]\n    B --> C[Generate Chef repo and provision cookbook]\n    C --> D[Write default provisioning recipe]\n    D --> E[Generate Policyfile]\n    E --> F[Run chef install]\n    F --> G[Run chef provision]\n    G --> H[EC2 instance created and converged]\n<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Set AWS environment variables<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The original article expects AWS values to be exported in the shell before provisioning.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php language-bash\">export AWS_ACCESS_KEY_ID=<span class=\"hljs-string\">\"YOUR_ACCESS_KEY_ID\"<\/span>\nexport AWS_SECRET_ACCESS_KEY=<span class=\"hljs-string\">\"YOUR_SECRET_ACCESS_KEY\"<\/span>\nexport AWS_DEFAULT_REGION=<span class=\"hljs-string\">\"us-east-1\"<\/span>\nexport AWS_SSH_KEY=<span class=\"hljs-string\">\"your_ec2_key_pair_name\"<\/span>\n\n<span class=\"hljs-comment\"># Legacy aliases used by some older examples<\/span>\nexport AWS_ACCESS_KEY=<span class=\"hljs-string\">\"$AWS_ACCESS_KEY_ID\"<\/span>\nexport AWS_SECRET_KEY=<span class=\"hljs-string\">\"$AWS_SECRET_ACCESS_KEY\"<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Variable<\/th><th>Purpose<\/th><th>In original workflow<\/th><\/tr><\/thead><tbody><tr><td>AWS_ACCESS_KEY_ID<\/td><td>AWS API access key<\/td><td>Required<\/td><\/tr><tr><td>AWS_SECRET_ACCESS_KEY<\/td><td>AWS API secret key<\/td><td>Required<\/td><\/tr><tr><td>AWS_DEFAULT_REGION<\/td><td>Default region for AWS API calls<\/td><td>Required<\/td><\/tr><tr><td>AWS_SSH_KEY<\/td><td>EC2 key pair name used at launch<\/td><td>Required by sample recipe options<\/td><\/tr><tr><td>AWS_ACCESS_KEY \/ AWS_SECRET_KEY<\/td><td>Legacy alias variable names<\/td><td>Optional compatibility aliases<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Generate the repo and provisioning cookbook<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Create a new repository and then add the provisioning cookbook used by this flow.<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs language-bash\">chef generate repo chefdk-provision-demo\ncd chefdk-provision-demo\nchef generate cookbook provision<\/code><\/span><\/pre>\n\n\n<h2 class=\"wp-block-heading\">Author the default provisioning recipe<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Edit <code>provision\/recipes\/default.rb<\/code> and define your driver and machine options. The structure below mirrors the original article, with placeholders where appropriate.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php language-ruby\">context = ChefDK::ProvisioningData.context\n\nwith_driver <span class=\"hljs-string\">'aws::us-west-2'<\/span>\n\noptions = {\n  ssh_username: <span class=\"hljs-string\">'admin'<\/span>,\n  use_private_ip_for_ssh: <span class=\"hljs-keyword\">false<\/span>,\n  bootstrap_options: {\n    key_name: ENV&#91;<span class=\"hljs-string\">'AWS_SSH_KEY'<\/span>],\n    image_id: <span class=\"hljs-string\">'ami-xxxxxxxx'<\/span>,\n    instance_type: <span class=\"hljs-string\">'m3.medium'<\/span>\n  },\n  convergence_options: context.convergence_options\n}\n\nmachine context.node_name <span class=\"hljs-keyword\">do<\/span>\n  machine_options options\n  action context.action\n  converge <span class=\"hljs-keyword\">true<\/span>\nend<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Key points: the AWS driver selects the region, bootstrap options define how the EC2 instance is created, and convergence options apply the intended Chef run list during provisioning.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Create and install a Policyfile<\/h2>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php language-bash\">chef generate policyfile\n$EDITOR Policyfile.rb<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript language-ruby\">name <span class=\"hljs-string\">\"chefdk-provision-demo\"<\/span>\n<span class=\"hljs-attr\">default_source<\/span> :community\nrun_list <span class=\"hljs-string\">\"recipe&#91;libuuid-user]\"<\/span>\ncookbook <span class=\"hljs-string\">\"libuuid-user\"<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Install the Policyfile so dependencies are resolved and lock data is created before provisioning.<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs language-bash\">chef install<\/code><\/span><\/pre>\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Command<\/th><th>Purpose<\/th><th>Main output<\/th><\/tr><\/thead><tbody><tr><td>chef generate repo chefdk-provision-demo<\/td><td>Initialize a Chef repository<\/td><td>Repo directory structure<\/td><\/tr><tr><td>chef generate cookbook provision<\/td><td>Create provisioning cookbook<\/td><td>provision\/ cookbook files<\/td><\/tr><tr><td>chef generate policyfile<\/td><td>Create Policyfile template<\/td><td>Policyfile.rb<\/td><\/tr><tr><td>chef install<\/td><td>Resolve cookbook dependencies<\/td><td>Policyfile.lock.json and local cache<\/td><\/tr><tr><td>chef provision testing &#8211;sync -n debian-libuuid<\/td><td>Launch and converge target node<\/td><td>Provisioned EC2 instance with applied policy<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Provision the EC2 instance<\/h2>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs language-bash\">chef provision testing --sync -n debian-libuuid<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">This command performs synchronization and converges the node name specified with <code>-n<\/code>. Validate region, key pair name, AMI, and SSH user before running.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Troubleshooting checkpoints<\/h2>\n\n\n\n<ul class=\"wp-block-list\"><li>Credentials exported in the same shell session running Chef commands.<\/li><li>EC2 key pair exists in the same AWS region used by the driver.<\/li><li>AMI ID is valid and available in that region\/account.<\/li><li>SSH username matches the selected AMI defaults.<\/li><li>IAM permissions allow EC2 instance creation, tagging, and describe actions.<\/li><\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Reference and attribution<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Original reference cited in the source post: <a href=\"http:\/\/jtimberman.housepub.org\/blog\/2015\/05\/15\/quick-tip-chefdk-provision\/\" target=\"_blank\" rel=\"noopener noreferrer\">http:\/\/jtimberman.housepub.org\/blog\/2015\/05\/15\/quick-tip-chefdk-provision\/<\/a><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Best practices for maintaining this legacy workflow<\/h2>\n\n\n\n<ul class=\"wp-block-list\"><li>Keep credentials out of shell history; prefer short-lived credentials or profile-based auth where possible.<\/li><li>Parameterize AMI ID, instance type, region, and SSH username instead of hardcoding.<\/li><li>Commit <code>Policyfile.rb<\/code> and lockfile for reproducible convergence.<\/li><li>Run provisioning first in a non-production AWS account to validate recipe and bootstrap behavior.<\/li><\/ul>\n","protected":false},"excerpt":{"rendered":"<p>A practical, step-by-step walkthrough for provisioning an AWS EC2 VM using the classic ChefDK and chef-provisioning approach. Includes workflow, commands, recipe examples, and validation checks for legacy environments.<\/p>\n","protected":false},"author":1,"featured_media":3108,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_joinchat":[],"footnotes":""},"categories":[5633],"tags":[162,1399,1400,1404,1405,407,1122,545,1402,593,1397,1401,1403,1406,732,213,968,662],"class_list":["post-452","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-aws","tag-aws","tag-aws-ec2","tag-aws-ec2-vm","tag-aws-ec2-vm-guide","tag-aws-ec2-vm-tutorial","tag-chef","tag-guide","tag-how","tag-how-to-provision-a-aws-ec2-vm","tag-process","tag-provision","tag-provision-a-aws-ec2-vm-using-chef","tag-provision-a-aws-ec2-vm-using-chef-tutorials","tag-provisioning-aws-ec2-vm","tag-steps","tag-tutorial","tag-virtual-machine","tag-vm"],"_links":{"self":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts\/452","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/comments?post=452"}],"version-history":[{"count":5,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts\/452\/revisions"}],"predecessor-version":[{"id":79328,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts\/452\/revisions\/79328"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/media\/3108"}],"wp:attachment":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/media?parent=452"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/categories?post=452"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/tags?post=452"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}