Skip to content

Commit

Permalink
Pulling together existing PRs for windows support, includes
Browse files Browse the repository at this point in the history
  • Loading branch information
tyler-ball committed Jun 9, 2015
1 parent 3c9ff2b commit 00e18b1
Show file tree
Hide file tree
Showing 4 changed files with 142 additions and 35 deletions.
16 changes: 13 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,13 @@ Please read the [Driver usage][driver_usage] page for more details.
## Default Configuration

This driver can determine AMI and username login for a select number of
platforms in each region. Currently, the following platform names are
supported:
platforms in each region.

For Windows instances the generated Administrator password is fetched
automatically from Amazon EC2 with the same private key as we use for
SSH logins to Linux.

Currently, the following platform names are supported:

```ruby
---
Expand All @@ -45,15 +50,18 @@ platforms:
- name: ubuntu-10.04
driver:
image_id: ami-1ab3ce73
transport:
username: ubuntu
- name: ubuntu-12.04
driver:
image_id: ami-2f115c46
transport:
username: ubuntu
# ...
- name: centos-6.4
driver:
image_id: ami-bf5021d6
transport:
username: root
# ...
```
Expand Down Expand Up @@ -387,11 +395,13 @@ platforms:
- name: ubuntu-12.04
driver:
image_id: ami-fd20ad94
username: ubuntu
- name: centos-6.3
driver:
image_id: ami-ef5ff086
transport:
username: ec2-user
- name: windows-2012r2
- name: windows-2008r2
suites:
# ...
Expand Down
39 changes: 29 additions & 10 deletions data/amis.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@
"ubuntu-13.10": "ami-45f1a744",
"ubuntu-14.04": "ami-955c0c94",
"centos-6.4": "ami-9ffa709e",
"debian-7.1.0": "ami-f1f064f0"
"debian-7.1.0": "ami-f1f064f0",
"windows-2012r2": "ami-28bc7428",
"windows-2008r2": "ami-5ace065a"
},
"ap-southeast-1": {
"ubuntu-10.04": "ami-34713866",
Expand All @@ -18,7 +20,9 @@
"ubuntu-13.10": "ami-02e6b850",
"ubuntu-14.04": "ami-9a7c25c8",
"centos-6.4": "ami-46f5bb14",
"debian-7.1.0": "ami-fe8ac3ac"
"debian-7.1.0": "ami-fe8ac3ac",
"windows-2012r2": "ami-062e1054",
"windows-2008r2": "ami-30291762"
},
"ap-southeast-2": {
"ubuntu-10.04": "ami-2f009315",
Expand All @@ -28,7 +32,9 @@
"ubuntu-13.10": "ami-e54423df",
"ubuntu-14.04": "ami-f3b1d6c9",
"centos-6.4": "ami-9352c1a9",
"debian-7.1.0": "ami-4e099a74"
"debian-7.1.0": "ami-4e099a74",
"windows-2012r2": "ami-6be19e51",
"windows-2008r2": "ami-fdd8a7c7"
},
"eu-west-1": {
"ubuntu-10.04": "ami-bbadb0cf",
Expand All @@ -38,7 +44,9 @@
"ubuntu-13.10": "ami-39eb3f4e",
"ubuntu-14.04": "ami-c112c5b6",
"centos-6.4": "ami-75190b01",
"debian-7.1.0": "ami-954559e1"
"debian-7.1.0": "ami-954559e1",
"windows-2012r2": "ami-1387ed64",
"windows-2008r2": "ami-1b97fd6c"
},
"sa-east-1": {
"ubuntu-10.04": "ami-962c898b",
Expand All @@ -48,7 +56,9 @@
"ubuntu-13.10": "ami-076cc21a",
"ubuntu-14.04": "ami-052b8518",
"centos-6.4": "ami-a665c0bb",
"debian-7.1.0": "ami-b03590ad"
"debian-7.1.0": "ami-b03590ad",
"windows-2012r2": "ami-7929ae64",
"windows-2008r2": "ami-9331b68e"
},
"us-east-1": {
"ubuntu-10.04": "ami-1ab3ce73",
Expand All @@ -58,7 +68,9 @@
"ubuntu-13.10": "ami-a65393ce",
"ubuntu-14.04": "ami-4a915c22",
"centos-6.4": "ami-bf5021d6",
"debian-7.1.0": "ami-50d9a439"
"debian-7.1.0": "ami-50d9a439",
"windows-2012r2": "ami-c01102a8",
"windows-2008r2": "ami-c8c0d3a0"
},
"us-west-1": {
"ubuntu-10.04": "ami-848ba2c1",
Expand All @@ -68,7 +80,9 @@
"ubuntu-13.10": "ami-bb2a2afe",
"ubuntu-14.04": "ami-d99a9a9c",
"centos-6.4": "ami-5d456c18",
"debian-7.1.0": "ami-1a9bb25f"
"debian-7.1.0": "ami-1a9bb25f",
"windows-2012r2": "ami-c30a39f3",
"windows-2008r2": "ami-af04eaeb"
},
"us-west-2": {
"ubuntu-10.04": "ami-f19407c1",
Expand All @@ -78,7 +92,9 @@
"ubuntu-13.10": "ami-c18ff1f1",
"ubuntu-14.04": "ami-b7720b87",
"centos-6.4": "ami-b3bf2f83",
"debian-7.1.0": "ami-158a1925"
"debian-7.1.0": "ami-158a1925",
"windows-2012r2": "ami-830ee0c7",
"windows-2008r2": "ami-adf8cb9d"
}
},
"usernames": {
Expand All @@ -89,11 +105,14 @@
"ubuntu-13.10": "ubuntu",
"ubuntu-14.04": "ubuntu",
"centos-6.4": "root",
"debian-7.1.0": "admin"
"debian-7.1.0": "admin",
"windows-2008r2": "administrator",
"windows-2012r2": "administrator"
},
"references": {
"ubuntu": "http://uec-images.ubuntu.com/query/",
"debian": "https://wiki.debian.org/Cloud/AmazonEC2Image",
"centos": "http://wiki.centos.org/Cloud/AWS"
"centos": "http://wiki.centos.org/Cloud/AWS",
"windows": "http://aws.amazon.com/windows/"
}
}
1 change: 0 additions & 1 deletion lib/kitchen/driver/aws/instance_generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.

require "kitchen/logging"
require "base64"

module Kitchen
Expand Down
121 changes: 100 additions & 21 deletions lib/kitchen/driver/ec2.rb
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,11 @@ class Ec2 < Kitchen::Driver::Base # rubocop:disable Metrics/ClassLength
default_config :ebs_optimized, false
default_config :security_group_ids, nil
default_config :tags, "created-by" => "test-kitchen"
default_config :user_data, nil
default_config :user_data do |driver|
if driver.windows_os?
driver.default_windows_user_data
end
end
default_config :private_ip_address, nil
default_config :iam_profile_name, nil
default_config :price, nil
Expand Down Expand Up @@ -200,6 +204,14 @@ def create(state) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
info("EC2 instance <#{state[:server_id]}> created.")
wait_until_ready(server, state)

if windows_os? &&
instance.transport[:username] =~ /administrator/i &&
instance.transport[:password].nil?
# If we're logging into the administrator user and a password isn't
# supplied, try to fetch it from the AWS instance
fetch_windows_admin_password(server, state)
end

info("EC2 instance <#{state[:server_id]}> ready.")
instance.transport.connection(state).wait_until_ready
create_ec2_json(state)
Expand Down Expand Up @@ -322,31 +334,53 @@ def wait_until_ready(server, state)
# we still have it, even if it will change later
state[:hostname] = hostname
# Euca instances often report ready before they have an IP
aws_instance.exists? &&
ready = aws_instance.exists? &&
aws_instance.state.name == "running" &&
hostname != "0.0.0.0"
if ready && windows_os?
output = server.console_output.output
unless output.nil?
output = Base64.decode64(output)
debug "Console output: --- \n#{output}"
end
ready = !!(output =~ /Windows is Ready to use/)
end
ready
end
end

def fetch_windows_admin_password(server, state)
wait_with_destroy(server, state, "to fetch windows admin password") do |aws_instance|
enc = server.client.get_password_data(
instance_id: state[:server_id]
).password_data
# Password data is blank until password is available
!enc.nil? && !enc.empty?
end
pass = server.decrypt_windows_password(instance.transport[:ssh_key])
state[:password] = pass
info("Retrieved Windows password for instance <#{state[:server_id]}>.")
end

def wait_with_destroy(server, state, status_msg, &block)
wait_log = proc do |attempts|
c = attempts * config[:retryable_sleep]
t = config[:retryable_tries] * config[:retryable_sleep]
info "Waited #{c}/#{t}s for instance <#{state[:server_id]}> #{status_msg}."
end
begin
server.wait_until(
:max_attempts => config[:retryable_tries],
:delay => config[:retryable_sleep],
:before_attempt => wait_log,
&block
)
rescue ::Aws::Waiters::Errors::WaiterFailed
error("Ran out of time waiting for the server with id [#{state[:server_id]}]" \
" #{status_msg}, attempting to destroy it")
destroy(state)
raise
end
c = attempts * config[:retryable_sleep]
t = config[:retryable_tries] * config[:retryable_sleep]
info "Waited #{c}/#{t}s for instance <#{state[:server_id]}> #{status_msg}."
end
begin
server.wait_until(
:max_attempts => config[:retryable_tries],
:delay => config[:retryable_sleep],
:before_attempt => wait_log,
&block
)
rescue ::Aws::Waiters::Errors::WaiterFailed
error("Ran out of time waiting for the server with id [#{state[:server_id]}]" \
" #{status_msg}, attempting to destroy it")
destroy(state)
raise
end
end

def amis
Expand Down Expand Up @@ -391,14 +425,59 @@ def hostname(server, interface_type = nil)
end

def create_ec2_json(state)
if instance.transport.windows_os?
cmd = "mkdir c:\\chef\\ohai\\hints; echo $null >> c:\\chef\\ohai\\hints\\ec2.json"
if windows_os?
cmd = "mkdir \\etc\\chef\\ohai\\hints; echo $null >> \\etc\\chef\\ohai\\hints\\ec2.json"
else
cmd = "sudo mkdir -p /etc/chef/ohai/hints;sudo touch /etc/chef/ohai/hints/ec2.json"
end
instance.transport.connection(state).execute(cmd)
end

def default_windows_user_data
#Preparing custom static admin user if we defined something other than Administrator
customAdminScript = ''
if !(instance.transport[:username] =~ /administrator/i) && instance.transport[:password]
customAdminScript = Kitchen::Util.outdent!(<<-EOH)
"Disabling Complex Passwords" >> $logfile
$seccfg = [IO.Path]::GetTempFileName()
& secedit.exe /export /cfg $seccfg >> $logfile
(Get-Content $seccfg) | Foreach-Object {$_ -replace "PasswordComplexity\\s*=\\s*1", "PasswordComplexity = 0"} | Set-Content $seccfg
& secedit.exe /configure /db $env:windir\\security\\new.sdb /cfg $seccfg /areas SECURITYPOLICY >> $logfile
& cp $seccfg "c:\\"
& del $seccfg
$username="#{instance.transport[:username]}"
$password="#{instance.transport[:password]}"
"Creating static user: $username" >> $logfile
& net.exe user /y /add $username $password >> $logfile
"Adding $username to Administrators" >> $logfile
& net.exe localgroup Administrators /add $username >> $logfile
EOH
end

# Returning the fully constructed PowerShell script to user_data
Kitchen::Util.outdent!(<<-EOH)
<powershell>
$logfile="C:\\Program Files\\Amazon\\Ec2ConfigService\\Logs\\kitchen-ec2.log"
#PS Remoting and & winrm.cmd basic config
Enable-PSRemoting -Force -SkipNetworkProfileCheck
& winrm.cmd quickconfig -q >> $logfile
& winrm.cmd set winrm/config '@{MaxTimeoutms="1800000"}' >> $logfile
& winrm.cmd set winrm/config/winrs '@{MaxMemoryPerShellMB="512"}' >> $logfile
& winrm.cmd set winrm/config/winrs '@{MaxShellsPerUser="50"}' >> $logfile
#Client settings
& winrm.cmd set winrm/config/client/auth '@{Basic="true"}' >> $logfile
#Server settings
& winrm.cmd set winrm/config/service/auth '@{Basic="true"}' >> $logfile
& winrm.cmd set winrm/config/service '@{AllowUnencrypted="true"}' >> $logfile
& winrm.cmd set winrm/config/winrs '@{MaxMemoryPerShellMB="1024"}' >> $logfile
#Firewall Config
& netsh.exe advfirewall set publicprofile state off >> $logfile
& netsh advfirewall firewall set rule name="Windows Remote Management (HTTP-In)" profile=public protocol=tcp localport=5985 remoteip=localsubnet new remoteip=any >> $logfile
#{customAdminScript}
</powershell>
EOH
end

end
end
end

0 comments on commit 00e18b1

Please sign in to comment.