Skip to content

Instantly share code, notes, and snippets.

@scottwb
Created December 13, 2012 14:37
Show Gist options
  • Save scottwb/4276748 to your computer and use it in GitHub Desktop.
Save scottwb/4276748 to your computer and use it in GitHub Desktop.
#!/usr/bin/env/ruby
require 'socket'
# AWS API Credentials
AWS_ACCESS_KEY_ID = "your-aws-access-key-id"
AWS_SECRET_ACCESS_KEY = "your-aws-secret-access-key"
# Node details
NODE_NAME = "webserver-01.example.com"
CHEF_ENVIRONMENT = "production"
INSTANCE_SIZE = "m1.large"
EBS_ROOT_VOL_SIZE = 70 # in GB
REGION = "us-west-2"
AVAILABILITY_ZONE = "us-west-2b"
AMI_NAME = "ami-46c54c76"
SECURITY_GROUP = "Web Servers"
RUN_LIST = "role[base],role[iis]"
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>
$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",
"--aws-access-key-id #{AWS_ACCESS_KEY_ID}",
"--aws-secret-access-key #{AWS_SECRET_ACCESS_KEY}",
"--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}",
"--groups '#{SECURITY_GROUP}'",
"--user-data #{USER_DATA_FILE}",
"--verbose"
].join(" ")
# Run `knife ec2 server create` to provision the new instance and
# read the output until we know it's public 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 =~ /^Public IP Address: (.*)$/
ip_addr = $1.strip
Process.kill("TERM", pipe.pid)
break
end
end
rescue EOFError
# done
end
end
if id_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..."
start_time = Time.now
begin
s = TCPSocket.new ip_addr, 5985
rescue Errno:ETIMEOUT => e
puts "Still waiting..."
retry
end
s.close
# 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
@H2so4
Copy link

H2so4 commented Apr 23, 2013

Wow this is awesome... Many thanks!!! can't wait to give it a shot... very clever implementation.

@F0RMaTC
Copy link

F0RMaTC commented Jul 4, 2014

this doesn't work...

if id_addr.nil? on linenumber 74 must me ip_addr

Besides that the following error occur:
./bootstrap-windows.rb:86: syntax error, unexpected tSYMBEG, expecting keyword_do or '{' or '('
rescue Errno:ETIMEOUT => e

@Iristyle
Copy link

May also need to open firewall ports

netsh advfirewall firewall add rule name="WinRM 5985" protocol=TCP dir=in localport=5985 action=allow
netsh advfirewall firewall add rule name="WinRM 5986" protocol=TCP dir=in localport=5986 action=allow

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