formatron 0.1.14 → 0.1.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. checksums.yaml +4 -4
  2. data/formatron.gemspec +2 -1
  3. data/lib/formatron.rb +23 -10
  4. data/lib/formatron/aws.rb +26 -13
  5. data/lib/formatron/chef.rb +53 -9
  6. data/lib/formatron/chef/berkshelf.rb +15 -11
  7. data/lib/formatron/chef/keys.rb +5 -9
  8. data/lib/formatron/chef/knife.rb +39 -30
  9. data/lib/formatron/chef/ssh.rb +4 -1
  10. data/lib/formatron/chef/winrm.rb +37 -0
  11. data/lib/formatron/chef_clients.rb +6 -4
  12. data/lib/formatron/cli/generators/credentials.rb +1 -1
  13. data/lib/formatron/cloud_formation/resources/ec2.rb +58 -57
  14. data/lib/formatron/cloud_formation/scripts.rb +75 -3
  15. data/lib/formatron/cloud_formation/template.rb +6 -0
  16. data/lib/formatron/cloud_formation/template/vpc.rb +8 -0
  17. data/lib/formatron/cloud_formation/template/vpc/subnet.rb +8 -0
  18. data/lib/formatron/cloud_formation/template/vpc/subnet/bastion.rb +12 -0
  19. data/lib/formatron/cloud_formation/template/vpc/subnet/chef_server.rb +12 -0
  20. data/lib/formatron/cloud_formation/template/vpc/subnet/instance.rb +13 -2
  21. data/lib/formatron/cloud_formation/template/vpc/subnet/instance/security_group.rb +47 -2
  22. data/lib/formatron/cloud_formation/template/vpc/subnet/instance/setup.rb +92 -27
  23. data/lib/formatron/cloud_formation/template/vpc/subnet/nat.rb +12 -0
  24. data/lib/formatron/dsl/formatron/global.rb +2 -0
  25. data/lib/formatron/dsl/formatron/global/windows.rb +17 -0
  26. data/lib/formatron/dsl/formatron/vpc/subnet/instance.rb +1 -0
  27. data/lib/formatron/external/dsl.rb +19 -0
  28. data/lib/formatron/util/winrm.rb +40 -0
  29. data/lib/formatron/version.rb +1 -1
  30. metadata +33 -16
@@ -1,8 +1,9 @@
1
1
  class Formatron
2
2
  module CloudFormation
3
3
  # Generates scripts for setting up instances with CloudFormation init
4
+ # rubocop:disable Metrics/ModuleLength
4
5
  module Scripts
5
- def self.hostname(sub_domain:, hosted_zone_name:)
6
+ def self.linux_common(sub_domain:, hosted_zone_name:)
6
7
  # rubocop:disable Metrics/LineLength
7
8
  <<-EOH.gsub(/^ {10}/, '')
8
9
  #/bin/bash -v
@@ -17,6 +18,75 @@ class Formatron
17
18
  # rubocop:enable Metrics/LineLength
18
19
  end
19
20
 
21
+ def self.windows_common(sub_domain:, hosted_zone_name:)
22
+ # rubocop:disable Metrics/LineLength
23
+ <<-EOH.gsub(/^ {10}/, '')
24
+ wmic computersystem where name="%COMPUTERNAME%" call rename name="#{sub_domain}"
25
+ REG ADD HKLM\\SYSTEM\\CurrentControlSet\\services\\Tcpip\\Parameters /v Domain /t REG_SZ /d #{hosted_zone_name} /f
26
+ shutdown.exe /r /t 00
27
+ EOH
28
+ # rubocop:enable Metrics/LineLength
29
+ end
30
+
31
+ # rubocop:disable Metrics/MethodLength
32
+ def self.windows_administrator(name:, password:)
33
+ # rubocop:disable Metrics/LineLength
34
+ <<-EOH.gsub(/^ {10}/, '')
35
+ $newAdminName = '#{name}'
36
+ $adminPassword = '#{password}'
37
+
38
+ # disable password policy
39
+ secedit /export /cfg c:\\secpol.cfg
40
+ (gc C:\\secpol.cfg).replace("PasswordComplexity = 1", "PasswordComplexity = 0") | Out-File C:\\secpol.cfg
41
+ secedit /configure /db c:\\windows\\security\\local.sdb /cfg c:\\secpol.cfg /areas SECURITYPOLICY
42
+ rm -force c:\\secpol.cfg -confirm:$false
43
+
44
+ # find the local administrator user
45
+ $computerName = $env:COMPUTERNAME
46
+ $computer = [ADSI] "WinNT://$computerName,Computer"
47
+ foreach ( $childObject in $computer.Children ) {
48
+ # Skip objects that are not users.
49
+ if ( $childObject.Class -ne "User" ) {
50
+ continue
51
+ }
52
+ $type = "System.Security.Principal.SecurityIdentifier"
53
+ $childObjectSID = new-object $type($childObject.objectSid[0],0)
54
+ if ( $childObjectSID.Value.EndsWith("-500") ) {
55
+ $adminName = $childObject.Name[0]
56
+
57
+ # set the new password
58
+ $adminUser = [ADSI] "WinNT://$computerName/$adminName,User"
59
+ $adminUser.SetPassword($adminPassword)
60
+
61
+ # set the new name
62
+ $user = Get-WMIObject Win32_UserAccount -Filter "Name='$adminName'"
63
+ $result = $user.Rename($newAdminName)
64
+
65
+ break
66
+ }
67
+ }
68
+ EOH
69
+ # rubocop:enable Metrics/LineLength
70
+ end
71
+ # rubocop:enable Metrics/MethodLength
72
+
73
+ # rubocop:disable Metrics/MethodLength
74
+ def self.windows_signal(wait_condition_handle:)
75
+ {
76
+ 'Fn::Join' => [
77
+ '', [
78
+ 'cfn-signal.exe -e 0 ',
79
+ {
80
+ 'Fn::Base64' => {
81
+ Ref: wait_condition_handle
82
+ }
83
+ }
84
+ ]
85
+ ]
86
+ }
87
+ end
88
+ # rubocop:enable Metrics/MethodLength
89
+
20
90
  # rubocop:disable Metrics/MethodLength
21
91
  def self.nat(cidr:)
22
92
  # rubocop:disable Metrics/LineLength
@@ -66,8 +136,9 @@ class Formatron
66
136
  set -e
67
137
 
68
138
  export HOME=/root
69
-
70
- source /tmp/formatron/script-variables
139
+ export PATH=$PATH:/usr/local/sbin/
140
+ export PATH=$PATH:/usr/sbin/
141
+ export PATH=$PATH:/sbin
71
142
 
72
143
  apt-get -y update
73
144
  apt-get -y install wget ntp cron git libfreetype6 libpng3 python-pip
@@ -134,5 +205,6 @@ class Formatron
134
205
  # rubocop:enable Metrics/ParameterLists
135
206
  # rubocop:enable Metrics/MethodLength
136
207
  end
208
+ # rubocop:enable Metrics/ModuleLength
137
209
  end
138
210
  end
@@ -15,6 +15,8 @@ class Formatron
15
15
  external:,
16
16
  hosted_zone_name:,
17
17
  key_pair:,
18
+ administrator_name:,
19
+ administrator_password:,
18
20
  kms_key:,
19
21
  hosted_zone_id:,
20
22
  target:
@@ -25,6 +27,8 @@ class Formatron
25
27
  @external_outputs = external.outputs
26
28
  @hosted_zone_name = hosted_zone_name
27
29
  @key_pair = key_pair
30
+ @administrator_name = administrator_name
31
+ @administrator_password = administrator_password
28
32
  @kms_key = kms_key
29
33
  @hosted_zone_id = hosted_zone_id
30
34
  @bucket = formatron.bucket
@@ -45,6 +49,8 @@ class Formatron
45
49
  external: @external_formatron.vpc[key],
46
50
  hosted_zone_name: @hosted_zone_name,
47
51
  key_pair: @key_pair,
52
+ administrator_name: @administrator_name,
53
+ administrator_password: @administrator_password,
48
54
  kms_key: @kms_key,
49
55
  hosted_zone_id: @hosted_zone_id,
50
56
  bucket: @bucket,
@@ -23,6 +23,8 @@ class Formatron
23
23
  external:,
24
24
  hosted_zone_name:,
25
25
  key_pair:,
26
+ administrator_name:,
27
+ administrator_password:,
26
28
  kms_key:,
27
29
  hosted_zone_id:,
28
30
  bucket:,
@@ -33,6 +35,8 @@ class Formatron
33
35
  @external = external
34
36
  @hosted_zone_name = hosted_zone_name
35
37
  @key_pair = key_pair
38
+ @administrator_name = administrator_name
39
+ @administrator_password = administrator_password
36
40
  @kms_key = kms_key
37
41
  @hosted_zone_id = hosted_zone_id
38
42
  @bucket = bucket
@@ -72,6 +76,8 @@ class Formatron
72
76
  vpc_guid: @guid,
73
77
  vpc_cidr: @cidr,
74
78
  key_pair: @key_pair,
79
+ administrator_name: @administrator_name,
80
+ administrator_password: @administrator_password,
75
81
  hosted_zone_name: @hosted_zone_name,
76
82
  kms_key: @kms_key,
77
83
  nats: Util::VPC.instances(:nat, @vpc),
@@ -104,6 +110,8 @@ class Formatron
104
110
  vpc_guid: @guid,
105
111
  vpc_cidr: @cidr,
106
112
  key_pair: @key_pair,
113
+ administrator_name: @administrator_name,
114
+ administrator_password: @administrator_password,
107
115
  hosted_zone_name: @hosted_zone_name,
108
116
  kms_key: @kms_key,
109
117
  nats: Util::VPC.instances(:nat, @external, @vpc),
@@ -25,6 +25,8 @@ class Formatron
25
25
  vpc_guid:,
26
26
  vpc_cidr:,
27
27
  key_pair:,
28
+ administrator_name:,
29
+ administrator_password:,
28
30
  hosted_zone_name:,
29
31
  kms_key:,
30
32
  nats:,
@@ -41,6 +43,8 @@ class Formatron
41
43
  @cidr = @subnet.cidr
42
44
  @acl = @subnet.acl
43
45
  @key_pair = key_pair
46
+ @administrator_name = administrator_name
47
+ @administrator_password = administrator_password
44
48
  @hosted_zone_name = hosted_zone_name
45
49
  @kms_key = kms_key
46
50
  @nats = nats
@@ -78,6 +82,8 @@ class Formatron
78
82
  args = {
79
83
  symbol => instance,
80
84
  key_pair: @key_pair,
85
+ administrator_name: @administrator_name,
86
+ administrator_password: @administrator_password,
81
87
  availability_zone: @availability_zone,
82
88
  subnet_guid: @guid,
83
89
  hosted_zone_name: @hosted_zone_name,
@@ -116,6 +122,8 @@ class Formatron
116
122
  args = {
117
123
  symbol => instance,
118
124
  key_pair: @key_pair,
125
+ administrator_name: @administrator_name,
126
+ administrator_password: @administrator_password,
119
127
  availability_zone: @availability_zone,
120
128
  subnet_guid: @guid,
121
129
  hosted_zone_name: @hosted_zone_name,
@@ -12,6 +12,8 @@ class Formatron
12
12
  def initialize(
13
13
  bastion:,
14
14
  key_pair:,
15
+ administrator_name:,
16
+ administrator_password:,
15
17
  availability_zone:,
16
18
  subnet_guid:,
17
19
  hosted_zone_name:,
@@ -25,10 +27,13 @@ class Formatron
25
27
  target:
26
28
  )
27
29
  @bastion = bastion
30
+ _set_os
28
31
  _add_open_ports
29
32
  @instance = Instance.new(
30
33
  instance: bastion,
31
34
  key_pair: key_pair,
35
+ administrator_name: administrator_name,
36
+ administrator_password: administrator_password,
32
37
  availability_zone: availability_zone,
33
38
  subnet_guid: subnet_guid,
34
39
  hosted_zone_name: hosted_zone_name,
@@ -45,6 +50,12 @@ class Formatron
45
50
  # rubocop:enable Metrics/ParameterLists
46
51
  # rubocop:enable Metrics/MethodLength
47
52
 
53
+ def _set_os
54
+ @bastion.os(
55
+ 'ubuntu'
56
+ )
57
+ end
58
+
48
59
  def _add_open_ports
49
60
  @bastion.security_group do |security_group|
50
61
  security_group.open_tcp_port 22
@@ -56,6 +67,7 @@ class Formatron
56
67
  end
57
68
 
58
69
  private(
70
+ :_set_os,
59
71
  :_add_open_ports
60
72
  )
61
73
  end
@@ -20,6 +20,8 @@ class Formatron
20
20
  def initialize(
21
21
  chef_server:,
22
22
  key_pair:,
23
+ administrator_name:,
24
+ administrator_password:,
23
25
  availability_zone:,
24
26
  subnet_guid:,
25
27
  hosted_zone_name:,
@@ -70,6 +72,7 @@ class Formatron
70
72
  @organization_short_name = organization.short_name
71
73
  @organization_full_name = organization.full_name
72
74
  _set_default_instance_type
75
+ _set_os
73
76
  _add_ssl_cert_policy
74
77
  _add_keys_policy
75
78
  _add_open_ports
@@ -77,6 +80,8 @@ class Formatron
77
80
  @instance = Instance.new(
78
81
  instance: @chef_server,
79
82
  key_pair: key_pair,
83
+ administrator_name: administrator_name,
84
+ administrator_password: administrator_password,
80
85
  availability_zone: availability_zone,
81
86
  subnet_guid: subnet_guid,
82
87
  hosted_zone_name: hosted_zone_name,
@@ -100,6 +105,12 @@ class Formatron
100
105
  ) if @chef_server.instance_type.nil?
101
106
  end
102
107
 
108
+ def _set_os
109
+ @chef_server.os(
110
+ 'ubuntu'
111
+ )
112
+ end
113
+
103
114
  def _add_ssl_cert_policy
104
115
  @chef_server.policy do |policy|
105
116
  policy.statement do |statement|
@@ -190,6 +201,7 @@ class Formatron
190
201
 
191
202
  private(
192
203
  :_set_default_instance_type,
204
+ :_set_os,
193
205
  :_add_ssl_cert_policy,
194
206
  :_add_keys_policy,
195
207
  :_add_open_ports,
@@ -33,6 +33,8 @@ class Formatron
33
33
  def initialize(
34
34
  instance:,
35
35
  key_pair:,
36
+ administrator_name:,
37
+ administrator_password:,
36
38
  availability_zone:,
37
39
  subnet_guid:,
38
40
  hosted_zone_name:,
@@ -62,7 +64,10 @@ class Formatron
62
64
  "#{SecurityGroup::SECURITY_GROUP_PREFIX}#{@guid}"
63
65
  @availability_zone = availability_zone
64
66
  @instance_type = @instance.instance_type || 't2.micro'
67
+ @os = @instance.os || 'ubuntu'
65
68
  @key_pair = key_pair
69
+ @administrator_name = administrator_name
70
+ @administrator_password = administrator_password
66
71
  @subnet_guid = subnet_guid
67
72
  @subnet_id = "#{Subnet::SUBNET_PREFIX}#{@subnet_guid}"
68
73
  @sub_domain = @instance.sub_domain
@@ -108,6 +113,7 @@ class Formatron
108
113
  )
109
114
  policy.merge resources: resources
110
115
  security_group = SecurityGroup.new(
116
+ os: @os,
111
117
  security_group: @security_group,
112
118
  instance_guid: @guid,
113
119
  vpc_guid: @vpc_guid,
@@ -121,17 +127,22 @@ class Formatron
121
127
  availability_zone: @availability_zone,
122
128
  instance_type: @instance_type,
123
129
  key_name: @key_pair,
130
+ administrator_name: @administrator_name,
131
+ administrator_password: @administrator_password,
124
132
  subnet: @subnet_id,
125
133
  name: "#{@sub_domain}.#{@hosted_zone_name}",
126
134
  wait_condition_handle: @wait_condition_handle_id,
127
135
  security_group: @security_group_id,
128
136
  logical_id: @instance_id,
129
- source_dest_check: @source_dest_check
137
+ source_dest_check: @source_dest_check,
138
+ os: @os
130
139
  )
131
140
  setup = Setup.new(
132
141
  setup: @setup,
133
142
  sub_domain: @sub_domain,
134
- hosted_zone_name: @hosted_zone_name
143
+ hosted_zone_name: @hosted_zone_name,
144
+ os: @os,
145
+ wait_condition_handle: @wait_condition_handle_id
135
146
  )
136
147
  setup.merge instance: instance
137
148
  block_devices = BlockDevices.new(
@@ -7,16 +7,19 @@ class Formatron
7
7
  class Subnet
8
8
  class Instance
9
9
  # generates CloudFormation security group resource
10
+ # rubocop:disable Metrics/ClassLength
10
11
  class SecurityGroup
11
12
  SECURITY_GROUP_PREFIX = 'securityGroup'
12
13
 
13
14
  # rubocop:disable Metrics/MethodLength
14
15
  def initialize(
16
+ os:,
15
17
  security_group:,
16
18
  instance_guid:,
17
19
  vpc_guid:,
18
20
  vpc_cidr:
19
21
  )
22
+ @os = os
20
23
  @security_group = security_group
21
24
  @vpc_guid = vpc_guid
22
25
  @cidr = vpc_cidr
@@ -32,7 +35,11 @@ class Formatron
32
35
 
33
36
  # rubocop:disable Metrics/MethodLength
34
37
  def merge(resources:)
35
- ingress_rules = _base_ingress_rules
38
+ if @os.eql? 'windows'
39
+ ingress_rules = _base_windows_ingress_rules
40
+ else
41
+ ingress_rules = _base_ingress_rules
42
+ end
36
43
  ingress_rules.concat(
37
44
  @open_tcp_ports.collect do |port|
38
45
  {
@@ -104,11 +111,49 @@ class Formatron
104
111
  end
105
112
  # rubocop:enable Metrics/MethodLength
106
113
 
114
+ # rubocop:disable Metrics/MethodLength
115
+ def _base_windows_ingress_rules
116
+ [{
117
+ cidr: @cidr,
118
+ protocol: 'tcp',
119
+ from_port: '0',
120
+ to_port: '65535'
121
+ }, {
122
+ cidr: @cidr,
123
+ protocol: 'udp',
124
+ from_port: '0',
125
+ to_port: '65535'
126
+ }, {
127
+ cidr: @cidr,
128
+ protocol: 'icmp',
129
+ from_port: '-1',
130
+ to_port: '-1'
131
+ }, {
132
+ cidr: '0.0.0.0/0',
133
+ protocol: 'tcp',
134
+ from_port: '3389',
135
+ to_port: '3389'
136
+ }, {
137
+ cidr: '0.0.0.0/0',
138
+ protocol: 'tcp',
139
+ from_port: '5985',
140
+ to_port: '5985'
141
+ }, {
142
+ cidr: '0.0.0.0/0',
143
+ protocol: 'tcp',
144
+ from_port: '5986',
145
+ to_port: '5986'
146
+ }]
147
+ end
148
+ # rubocop:enable Metrics/MethodLength
149
+
107
150
  private(
108
151
  :_base_egress_rules,
109
- :_base_ingress_rules
152
+ :_base_ingress_rules,
153
+ :_base_windows_ingress_rules
110
154
  )
111
155
  end
156
+ # rubocop:enable Metrics/ClassLength
112
157
  end
113
158
  end
114
159
  end
@@ -7,52 +7,116 @@ class Formatron
7
7
  class Subnet
8
8
  class Instance
9
9
  # Adds setup scripts to an instance
10
+ # rubocop:disable Metrics/ClassLength
10
11
  class Setup
11
- def initialize(setup:, sub_domain:, hosted_zone_name:)
12
+ # rubocop:disable Metrics/MethodLength
13
+ def initialize(
14
+ setup:,
15
+ sub_domain:,
16
+ hosted_zone_name:,
17
+ os:,
18
+ wait_condition_handle:
19
+ )
12
20
  @setup = setup
21
+ @wait_condition_handle = wait_condition_handle
13
22
  @sub_domain = sub_domain
14
23
  @hosted_zone_name = hosted_zone_name
15
24
  @scripts = @setup.script unless @setup.nil?
16
25
  @variables = @setup.variable unless @setup.nil?
26
+ @os = os
17
27
  end
28
+ # rubocop:enable Metrics/MethodLength
18
29
 
19
30
  # rubocop:disable Metrics/MethodLength
20
31
  # rubocop:disable Metrics/AbcSize
21
32
  def merge(instance:)
22
- files = {
23
- '/tmp/formatron/script-0.sh' => {
24
- content: Scripts.hostname(
33
+ env = {}
34
+ @variables.each do |key, value|
35
+ env[key] = value.value
36
+ end unless @variables.nil?
37
+ if @os.eql? 'windows'
38
+ script_key = 'script-0'
39
+ script = "C:\\formatron\\#{script_key}.bat"
40
+ files = {}
41
+ commands = {}
42
+ @scripts.each_index do |index|
43
+ script_key = "script-#{index}"
44
+ script = "C:\\formatron\\#{script_key}.bat"
45
+ files[script] = {
46
+ content: @scripts[index]
47
+ }
48
+ commands[script_key] = {
49
+ command: script,
50
+ env: env
51
+ }
52
+ end unless @scripts.nil?
53
+ setup_script_index = @scripts.nil? ? 0 : @scripts.length
54
+ signal_script_index = setup_script_index + 1
55
+ script_key = "script-#{setup_script_index}"
56
+ script = "C:\\formatron\\#{script_key}.bat"
57
+ files[script] = {
58
+ content: Scripts.windows_common(
25
59
  sub_domain: @sub_domain,
26
60
  hosted_zone_name: @hosted_zone_name
27
- ),
28
- mode: '000755',
29
- owner: 'root',
30
- group: 'root'
61
+ )
31
62
  }
32
- }
33
- @scripts.each_index do |index|
34
- files["/tmp/formatron/script-#{index + 1}.sh"] = {
35
- content: @scripts[index],
36
- mode: '000755',
37
- owner: 'root',
38
- group: 'root'
63
+ commands[script_key] = {
64
+ command: script,
65
+ env: env,
66
+ waitAfterCompletion: 'forever'
39
67
  }
40
- end unless @scripts.nil?
41
- variables = []
42
- @variables.each do |key, value|
43
- variables.concat(["#{key}=", value.value, "\n"])
44
- end unless @variables.nil?
45
- files['/tmp/formatron/script-variables'] = {
46
- content: Template.join(*variables),
47
- mode: '000644',
48
- owner: 'root',
49
- group: 'root'
50
- } unless variables.length == 0
68
+ script_key = "script-#{signal_script_index}"
69
+ script = "C:\\formatron\\#{script_key}.bat"
70
+ files[script] = {
71
+ content: Scripts.windows_signal(
72
+ wait_condition_handle: @wait_condition_handle
73
+ )
74
+ }
75
+ commands[script_key] = {
76
+ command: script,
77
+ env: env
78
+ }
79
+ else
80
+ script_key = 'script-0'
81
+ script = "/tmp/formatron/#{script_key}.sh"
82
+ files = {
83
+ "#{script}" => {
84
+ content: Scripts.linux_common(
85
+ sub_domain: @sub_domain,
86
+ hosted_zone_name: @hosted_zone_name
87
+ ),
88
+ mode: '000755',
89
+ owner: 'root',
90
+ group: 'root'
91
+ }
92
+ }
93
+ commands = {
94
+ "#{script_key}" => {
95
+ command: script,
96
+ env: env
97
+ }
98
+ }
99
+ @scripts.each_index do |index|
100
+ script_key = "script-#{index + 1}"
101
+ script = "/tmp/formatron/#{script_key}.sh"
102
+ files[script] = {
103
+ content: @scripts[index],
104
+ mode: '000755',
105
+ owner: 'root',
106
+ group: 'root'
107
+ }
108
+ commands[script_key] = {
109
+ command: script,
110
+ env: env
111
+ }
112
+ end unless @scripts.nil?
113
+ end
51
114
  instance[:Metadata] = {
52
115
  Comment1: 'Create setup scripts',
53
116
  'AWS::CloudFormation::Init' => {
54
117
  config: {
55
- files: files
118
+ files: files,
119
+ commands: commands
56
120
  }
57
121
  }
58
122
  }
@@ -60,6 +124,7 @@ class Formatron
60
124
  # rubocop:enable Metrics/AbcSize
61
125
  # rubocop:enable Metrics/MethodLength
62
126
  end
127
+ # rubocop:enable Metrics/ClassLength
63
128
  end
64
129
  end
65
130
  end