Skip to content

Instantly share code, notes, and snippets.

@dhoer
Last active January 30, 2018 21:00
Show Gist options
  • Save dhoer/2ee1ae63a35542651024 to your computer and use it in GitHub Desktop.
Save dhoer/2ee1ae63a35542651024 to your computer and use it in GitHub Desktop.
Provision and Bootstrap Windows EC2 Instances With Chef

I borrowed this script from Scott Bradley's wonderful blog "Provision and Bootstrap Windows EC2 Instances With Chef," making the following changes:

  1. To make compatible with Windows Server 2012 R2, I added the following firewall rule to the powershell script section:

    netsh advfirewall firewall add rule name="WinRM in" protocol=TCP dir=in profile=any localport=5985 remoteip=any localip=any action=allow

  2. Used knife.rb instead of using AWS API credentials

  3. Added security-group-ids and subnet flags to provision to ec2 VPC

#!/usr/bin/env/ruby
require 'socket'
KNIFE_CONFIG = "~/.chef/knife.rb"
# Node details
NODE_NAME = "webserver-01.example.com"
CHEF_ENVIRONMENT = "production"
INSTANCE_SIZE = "m3.large"
EBS_ROOT_VOL_SIZE = 70 # in GB
REGION = "us-west-2"
AVAILABILITY_ZONE = "us-west-2b"
AMI_NAME = "ami-904be6f8" # Microsoft Windows Server 2012 R2 Base
SECURITY_GROUP_IDS = "sg-a1b2c3d4"
SUBNET = "subnet-a1b2c3d4"
RUN_LIST = "recipe[java]"
USER_DATA_FILE = "/tmp/userdata.txt"
USERNAME = "Administrator"
PASSWORD = "YourAdminPassword"
# Write user data file that sets up WinRM and sets the Administrator password.
File.open(USER_DATA_FILE, "w") do |f|
f.write <<EOT
<script>
winrm quickconfig -q & winrm set winrm/config/winrs @{MaxMemoryPerShellMB="300"} & winrm set winrm/config @{MaxTimeoutms="1800000"} & winrm set winrm/config/service @{AllowUnencrypted="true"} & winrm set winrm/config/service/auth @{Basic="true"}
</script>
<powershell>
netsh advfirewall firewall add rule name="WinRM in" protocol=TCP dir=in profile=any localport=5985 remoteip=any localip=any action=allow
$admin = [adsi]("WinNT://./administrator, user")
$admin.psbase.invoke("SetPassword", "#{PASSWORD}")
</powershell>
EOT
end
# Define the command to provision the instance
provision_cmd = [
"knife ec2 server create",
"--config '#{KNIFE_CONFIG}'",
"--tags 'Name=#{NODE_NAME}'",
"--environment '#{CHEF_ENVIRONMENT}'",
"--flavor #{INSTANCE_SIZE}",
"--ebs-size #{EBS_ROOT_VOL_SIZE}",
"--region #{REGION}",
"--availability-zone #{AVAILABILITY_ZONE}",
"--image #{AMI_NAME}",
"--security-group-ids '#{SECURITY_GROUP_IDS}'",
"--subnet '#{SUBNET}'",
"--user-data #{USER_DATA_FILE}",
"--verbose"
].join(" ")
puts provision_cmd
# Run `knife ec2 server create` to provision the new instance and
# read the output until we know it's VPC private IP address. At that point,
# knife is going to wait until the instance responds on the SSH port. Of
# course, being Windows, this will never happen, so we need to go ahead and
# kill knife and then proceed with the rest of this script to wait until
# WinRM is up and we can bootstrap the node with Chef over WinRM.
ip_addr = nil
IO.popen(provision_cmd) do |pipe|
begin
while line = pipe.readline
puts line
if line =~ /^Private IP Address: (.*)$/
ip_addr = $1.strip
Process.kill("TERM", pipe.pid)
break
end
end
rescue EOFError
# done
end
end
if ip_addr.nil?
puts "ERROR: Unable to get new instance's IP address"
exit -1
end
# Now the new instance is provisioned, but we have no idea when it will
# be ready to go. The first thing we'll do is wait until the WinRM port
# responds to connections.
puts "Waiting for WinRM..."
begin
TCPSocket.new(ip_addr, 5985).close
rescue Errno::ECONNREFUSED, Errno::ETIMEDOUT
puts "Still waiting..."
retry
end
# You'd think we'd be good to go now...but NOPE! There is still more Windows
# bootstrap crap going on, and we have no idea what we need to wait on. So,
# in a last-ditch effort to make this all work, we've seen that 120 seconds
# ought to be enough...
wait_time = 120
while wait_time > 0
puts "Better wait #{wait_time} more seconds..."
sleep 1
wait_time -= 1
end
puts "Finally ready to try bootstrapping instance..."
# Define the command to bootstrap the already-provisioned instance with Chef
bootstrap_cmd = [
"knife bootstrap windows winrm #{ip_addr}",
"-x #{USERNAME}",
"-P '#{PASSWORD}'",
"--environment #{CHEF_ENVIRONMENT}",
"--node-name #{NODE_NAME}",
"--run-list #{RUN_LIST}",
"--verbose"
].join(' ')
# Now we can bootstrap the instance with Chef and the configured run list.
status = system(bootstrap_cmd) ? 0 : -1
exit status
@Joeper214
Copy link

Can I see what is written on your knife.rb? because I am confused on what to put on that configuration. Thanks.

@gvorugan
Copy link

Can you please update knife.rb file here and how you ran this file from chef workstation in powershell?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment