Skip to content

Instantly share code, notes, and snippets.

@alex-williams
Last active January 28, 2016 02:56
Show Gist options
  • Select an option

  • Save alex-williams/dac25d83e24816194dcc to your computer and use it in GitHub Desktop.

Select an option

Save alex-williams/dac25d83e24816194dcc to your computer and use it in GitHub Desktop.
Bootstrapping a Windows 2008 R2, 2012 or 2012 R2 Server in an EC2 VPC with Chef
This gist allows you to bootstrap a new Windows 2008 R2, 2012, or 2012 R2 Server in
EC2, with Chef, dynamically, without the AMI knowing anything about Chef at all when launched.
You will need to 'pre-bake' an AMI, as ideally, you'll want the Windows Management Framework 4
installed on it, to give you Powershell 4. This hasn't been tested using anything less than
Powershell 4, so that's my 'minimum requirement recommendation'. I've also only done this with
Chef 12, as it's been out a long time now and I see no reason to go backwards with it. The other
reason you'll need to pre-bake the AMI, is that you'll need to configure EC2-Config on the machine
to run user-data at the next boot (Be aware, setting this feature only persists one reboot,
subsequent reboots will not run user-data code) and you'll need to open up the Powershell tools
for AWS console, at least once and run through the setup, so that our S3 download works in the
user-data and Bootstrap scripts. My personal recommendation, is to secure the S3 bucket with a
policy and assign an IAM role to your server, however, provided you haven't stored anything in
the bucket with passwords, or secret information stored within, you can also just make the bucket
public if you're not interested in securing it.
Combining this gist with the 'windows_ad' chef community cookbook, you can have a Windows server
launched, bootstrapped and on the domain, within 12 minutes (If using an appropriate instance
size and SSD drives). I've made a few edits to the windows_ad cookbook, to facilitate adding
directly to an OU and changing the machine name in the command that joins the domain, requiring
only a single reboot (It normally takes at least 2 to rename and add to the domain), on Windows
Server 2008 R2, 2012 and 2012 R2. I'm just cleaning up the code, before doing a pull request to
the maintainers. Hopefully they pull it in :) At present though, that cookbook will allow you to
join the domain, rename the instance and add the machine to an ou, with separate actions.
You'll need to go through the other files in this gist with find and replace, to change the following:
'your-company-name' to whatever your organisation name is in your Chef server
'yourdomain' to whatever your chef server domain name is
'sandbox' to whatever environment you intend to use on your chef server (Remove completely if none)
'windows-server' to an appropriate role name
'your-chef-bucket' to whatever your chef bucket name is.
You may also need to change the version number of the chef-client, but at the time of writing, it
was the latest stable release. I recommend putting the chef client msi in your chef bucket, so that
you're reliant on your own stable msi, in your own S3 bucket :) I prefer the approach of using a
stable version, than constantly grabbing latest and ending up with a mix-and-match situation on
my servers and this also mitigates any errors with someone accidentally putting the wrong msi in
an S3 bucket you don't control.
To execute these files, you'll need to put first-boot.json and Bootstrap-EC2-Windows.ps1 in your
chef bucket, along with your chef validator.pem, then copy the entire contents of ec2-user-data.ps1
into the 'user-data' field of a fresh Amazon server. Provided you've set EC2-Config on the Windows
Server, to run EC2 user data at boot, before making your AMI, it should run, download the Bootstrap
script and run it, which in turn, should run everything else.
# Bootstrapping Windows 2008/2008 R2/2012/2012 R2 in EC2, using Chef
# Copyright 2015, Brighter Technology Ltd
# Licence GPLv3
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# A parameter you can pass an environment variable to for Chef, when we run Chef for the
# first time, we will pass this environment variable to Chef. Comment out if not using
# Environments in Chef, or using Chef Solo, etc. Note you will also need to edit the line
# near the bottom of the script, which calls Chef for the first time.
param([string]$chefEnvironment = "environment")
# Setting up log files for this bootstrap script and the Chef initial run.
$log = 'c:\Bootstrap.log'
$chefbootstrap = 'c:\Chef-Bootstrap.log'
#Create directory to download our installers to.
New-Item -ItemType Directory -Path "C:\Scratch"
Set-Location -Path "C:\Scratch"
# Download and install chef, as a service, so that it starts up automatically on reboot
Add-Content $log -value "Downloading Chef Installer"
Read-S3Object -BucketName your-chef-bucket -Key 'chef-client-12.2.1-1.msi' -File 'C:\Scratch\chef-client-12.2.1-1.msi'
Add-Content $log -value "Running Chef Installer"
Start-Process -FilePath "msiexec.exe" -ArgumentList '/qn /i C:\Scratch\chef-client-12.2.1-1.msi ADDLOCAL="ChefClientFeature,ChefServiceFeature" /norestart' -Wait
SetX Path "${Env:Path};C:\opscode\chef\bin" /m
$Env:Path += ';C:\opscode\chef\bin'
Add-Content $log -value "Installed Chef"
# Get the validator key from S3
Read-S3Object -BucketName your-chef-bucket -Key 'your-company-validator.pem' -File 'C:\chef\your-company-validator.pem'
# Create required Chef files
# client.rb sets the ENVIRONMENT of the node, along with some basics.
New-Item c:\chef\client.rb -type file -force
Add-Content C:\chef\client.rb "log_level :info"
Add-Content C:\chef\client.rb "log_location STDOUT"
Add-Content C:\chef\client.rb "chef_server_url 'https://chef.yourdomain.com/organizations/your-company'"
Add-Content C:\chef\client.rb "validation_client_name 'your-company-validator'"
Add-Content C:\chef\client.rb "validation_key 'c:\chef\your-company-validator.pem'"
# The following works out a unique name for the instance, based on its IP address. If the instance has an IP of
# 10.0.0.1 for example, with this code, its name will be worked out as IP-10-0-0-1. This bit of code only works it
# out for the Chef Node Name property, but you can also then use the node name, to change the machine name of
# the instance before adding to the domain and/or even change the 'name' tag of the instance in EC2.
$newCompName = "IP"
$ip = ((Test-Connection $env:computername -Count 1).ipv4address).ipaddresstostring
$octets = $ip -split '\.'
foreach ($octet in $octets)
{
$newCompName = $newCompName + "-" + $octet
}
# Add the new machine name to client.rb so that it shows up properly in chef and doesn't conflict on bootstrap with an existing node.
Add-Content C:\chef\client.rb "node_name '$newCompName'"
# create knife.rb and add in the chef_server_url value, so we can bypass ssl verification.
New-Item c:\chef\knife.rb -type file -force
Add-Content C:\chef\knife.rb "chef_server_url 'https://chef.yourdomain.com/organizations/your-company'"
#Set the DNS servers so that we can join the Domain. If you're not adding the instance to a domain, comment it out.
#If Windows 6.2 (Windows 2012/Windows 8) or above, we can use Set-DnsClientServerAddress, if it's 2008R2/Windows 7 or below, we can't and must use wmi.
if([Environment]::OSVersion.Version -ge (new-object 'Version' 6,2)){
Set-DnsClientServerAddress -InterfaceAlias Ethernet -ServerAddresses 10.0.1.10,10.0.2.10
} else {
$newDNS = "10.0.1.10","10.0.2.10"
$NetworkAdapterConfig = Get-WMIObject -Class Win32_NetworkAdapterConfiguration -Filter "ipenabled = 'true'"
$NetworkAdapterConfig.SetDNSServerSearchOrder($newDNS)
# In 2008 R2, we need to set the DNS suffix list to include our domain, or our server will struggle to join the
# domain and rename the server at the same time (Meaning only one reboot, not two)
$suffixList = $NetworkAdapterConfig.DNSDomainSuffixSearchOrder
$NetworkAdapterConfig.DNSDomainSuffixSearchOrder = $suffixList + "spotmain.com"
}
Add-Content $log -value 'Setting DNS servers to 10.0.1.10 and 10.0.2.10'
# Are you using self signed SSL? This will get around the SSL verification issue with a self signed cert.
cd c:\chef
knife ssl check
knife ssl fetch
# Launch chef client, telling it which environment to use and pointing it at it's first-boot.json to get its role.
& 'C:\opscode\chef\bin\chef-client.bat' -E $chefEnvironment -j C:\chef\first-boot.json 2>&1 > "$chefbootstrap"
# Bootstrapping is done, we will need to reboot now to complete domain joins and machine re-name actions. If you
# did not add to a domain, or rename, you probably still want a reboot, just to set everything off as normal and
# verify the machine will survive future reboots.
Restart-Computer
<powershell>
while (-Not (Test-Connection -ComputerName 8.8.8.8 -Count 1 -ErrorAction SilentlyContinue))
{
Start-Sleep -Seconds 5
}
Read-S3Object -BucketName your-chef-bucket -Key Bootstrap-EC2-Windows.ps1 -File C:\Bootstrap-EC2-Windows.ps1
while (-Not (Get-Item -Path "C:\Bootstrap-EC2-Windows.ps1" -ErrorAction SilentlyContinue))
{
Start-Sleep -Seconds 1
}
Read-S3Object -BucketName your-chef-bucket -Key first-boot.json -File C:\chef\first-boot.json
Invoke-Expression "C:\Bootstrap-EC2-Windows.ps1 sandbox"
</powershell>
{
"run_list": ["role[windows-server]"]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment