inspec-iggy 0.2.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -4,8 +4,8 @@
4
4
  #
5
5
  # Copyright:: 2018, Chef Software, Inc <legal@chef.io>
6
6
  #
7
-
8
- # Iggy version
9
- module Iggy
10
- VERSION = "0.2.0".freeze
7
+ module InspecPlugins
8
+ module Iggy
9
+ VERSION = '0.4.0'.freeze
10
+ end
11
11
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: inspec-iggy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matt Ray
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-05-22 00:00:00.000000000 Z
11
+ date: 2018-10-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: inspec
@@ -16,20 +16,20 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '2.0'
19
+ version: '2.3'
20
20
  - - "<"
21
21
  - !ruby/object:Gem::Version
22
- version: 3.0.0
22
+ version: 4.0.0
23
23
  type: :runtime
24
24
  prerelease: false
25
25
  version_requirements: !ruby/object:Gem::Requirement
26
26
  requirements:
27
27
  - - ">="
28
28
  - !ruby/object:Gem::Version
29
- version: '2.0'
29
+ version: '2.3'
30
30
  - - "<"
31
31
  - !ruby/object:Gem::Version
32
- version: 3.0.0
32
+ version: 4.0.0
33
33
  description: Generate InSpec compliance profiles from Terraform by tagging instances
34
34
  and mapping Terraform to InSpec.
35
35
  email:
@@ -42,17 +42,14 @@ files:
42
42
  - README.md
43
43
  - inspec-iggy.gemspec
44
44
  - lib/inspec-iggy.rb
45
- - lib/inspec-iggy/cli.rb
46
- - lib/inspec-iggy/cloudformation.rb
45
+ - lib/inspec-iggy/cloudformation/cli_command.rb
46
+ - lib/inspec-iggy/cloudformation/parser.rb
47
47
  - lib/inspec-iggy/inspec_helper.rb
48
- - lib/inspec-iggy/terraform.rb
48
+ - lib/inspec-iggy/plugin.rb
49
+ - lib/inspec-iggy/profile.rb
50
+ - lib/inspec-iggy/terraform/cli_command.rb
51
+ - lib/inspec-iggy/terraform/parser.rb
49
52
  - lib/inspec-iggy/version.rb
50
- - test/bad.json
51
- - test/bjc-demo-aws-4.5.4.json
52
- - test/main.tf
53
- - test/outputs.tf
54
- - test/terraform.tfstate
55
- - test/variables.tf
56
53
  homepage: https://github.com/inspec/inspec-iggy
57
54
  licenses:
58
55
  - Apache-2.0
@@ -73,7 +70,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
73
70
  version: '0'
74
71
  requirements: []
75
72
  rubyforge_project:
76
- rubygems_version: 2.6.14.1
73
+ rubygems_version: 2.6.13
77
74
  signing_key:
78
75
  specification_version: 4
79
76
  summary: InSpec plugin to generate InSpec compliance profiles from Terraform.
@@ -1,53 +0,0 @@
1
- # encoding: utf-8
2
- #
3
- # Author:: Matt Ray (<matt@chef.io>)
4
- #
5
- # Copyright:: 2018, Chef Software, Inc <legal@chef.io>
6
- #
7
-
8
- require "inspec/plugins"
9
- require "thor"
10
-
11
- require "inspec-iggy/terraform"
12
-
13
- module Iggy
14
- class CLI < Thor
15
- namespace "terraform"
16
-
17
- map %w{-v --version} => "version"
18
-
19
- desc "version", "Display version information", hide: true
20
- def version
21
- say("Iggy v#{Iggy::VERSION}")
22
- end
23
-
24
- class_option :tfstate,
25
- :aliases => "-t",
26
- :desc => "Specify path to the input terraform.tfstate",
27
- :default => "terraform.tfstate"
28
-
29
- class_option :debug,
30
- :desc => "Verbose debugging messages",
31
- :type => :boolean,
32
- :default => false
33
-
34
- desc "generate [options]", "Generate InSpec compliance controls from terraform.tfstate"
35
- def generate
36
- Inspec::Log.level = :debug if options[:debug]
37
- generated_controls = Iggy::Terraform.parse_generate(options[:tfstate])
38
- # let's just generate a control file with a set of controls for now
39
- Iggy::InspecHelper.print_controls(options[:tfstate], generated_controls)
40
- exit 0
41
- end
42
-
43
- desc "extract [options]", "Extract tagged InSpec profiles from terraform.tfstate"
44
- def extract
45
- Inspec::Log.level = :debug if options[:debug]
46
- extracted_profiles = Iggy::Terraform.parse_extract(options[:tfstate])
47
- Iggy::InspecHelper.print_commands(extracted_profiles)
48
- exit 0
49
- end
50
- end
51
-
52
- Inspec::Plugins::CLI.add_subcommand(CLI, "terraform", "terraform SUBCOMMAND ...", "Extract or generate InSpec from Terraform", {})
53
- end
@@ -1,104 +0,0 @@
1
- #
2
- # Author:: Matt Ray (<matt@chef.io>)
3
- #
4
- # Copyright:: 2018, Chef Software, Inc <legal@chef.io>
5
- #
6
-
7
- require "iggy"
8
-
9
- require "json"
10
- require "thor"
11
-
12
- module Iggy
13
- class CloudFormation < Thor
14
- option :template,
15
- :aliases => "-t",
16
- :desc => "Specify path to the input CloudFormation template"
17
-
18
- option :debug,
19
- :desc => "Verbose debugging messages",
20
- :type => :boolean,
21
- :default => false
22
-
23
- desc "generate [options]", "Generate InSpec compliance controls from CloudFormation template"
24
- long_desc <<-LONGDESC
25
- Reads in a CloudFormation JSON file and generates matching InSpec compliance controls.
26
- LONGDESC
27
- def generate
28
- Iggy::Log.level = :debug if options[:debug]
29
- Iggy::Log.debug "CloudFormation.generate file = #{options[:template]}"
30
- # hash of generated controls
31
- generated_controls = parse_generate(options[:template])
32
- Iggy::Log.debug "CloudFormation.generate generated_controls = #{generated_controls}"
33
- # let's just generate a control file with a set of controls for now
34
- Iggy::Inspec.print_controls(options[:template], generated_controls)
35
- exit 0
36
- end
37
-
38
- private
39
-
40
- def parse_generate(file)
41
- Iggy::Log.debug "CloudFormation.parse_generate file = #{file}"
42
- begin
43
- unless File.file?(file)
44
- STDERR.puts "ERROR: #{file} is an invalid file, please check your path."
45
- exit(-1)
46
- end
47
- template = JSON.parse(File.read(file))
48
- rescue JSON::ParserError => e
49
- STDERR.puts e.message
50
- STDERR.puts "ERROR: Parsing error in #{file}."
51
- exit(-1)
52
- end
53
- basename = File.basename(file)
54
- absolutename = File.absolute_path(file)
55
-
56
- # InSpec controls generated
57
- generated_controls = {}
58
-
59
- # iterate over the resources
60
- cfn_resources = template["Resources"]
61
- # iterate over the Resources, use these as IDs?
62
- cfn_resources.keys.each do |cfn_res|
63
- # split out the last ::, these are all AWS
64
- cfn_res_type = "aws_" + cfn_resources[cfn_res]["Type"].split("::").last.downcase
65
-
66
- # does this match an InSpec resource?
67
- if Inspec::RESOURCES.include?(cfn_res_type)
68
- Iggy::Log.debug "CloudFormation.parse_generate cfn_res_type = #{cfn_res_type} MATCH"
69
- # insert new control based off the resource's ID
70
- generated_controls[cfn_res] = {}
71
- generated_controls[cfn_res]["name"] = "#{cfn_res_type}::#{cfn_res}"
72
- generated_controls[cfn_res]["title"] = "Iggy #{basename} #{cfn_res_type}::#{cfn_res}"
73
- generated_controls[cfn_res]["desc"] = "#{cfn_res_type}::#{cfn_res} from the source file #{absolutename}\nGenerated by Iggy v#{Iggy::VERSION}"
74
- generated_controls[cfn_res]["impact"] = "1.0"
75
- generated_controls[cfn_res]["resource"] = cfn_res_type
76
- generated_controls[cfn_res]["parameter"] = cfn_res
77
- generated_controls[cfn_res]["tests"] = []
78
- generated_controls[cfn_res]["tests"][0] = "it { should exist }"
79
-
80
- # if there's a match, see if there are matching InSpec properties
81
- inspec_properties = Iggy::Inspec.resource_properties(cfn_res_type)
82
-
83
- cfn_resources[cfn_res]["Properties"].keys.each do |attr|
84
- # insert '_' on the CamelCase to get camel_case
85
- attr_split = attr.split /(?=[A-Z])/
86
- property = attr_split.join("_").downcase
87
- if inspec_properties.member?(property)
88
- Iggy::Log.debug "CloudFormation.parse_generate #{cfn_res_type} inspec_property = #{property} MATCH"
89
- value = cfn_resources[cfn_res]["Properties"][attr]
90
- # skip the {"Ref"=>"VPC"} for now
91
- generated_controls[cfn_res]["tests"].push("its('#{property}') { should cmp '#{value}' }") unless value.is_a? Hash
92
- else
93
- Iggy::Log.debug "CloudFormation.parse_generate #{cfn_res_type} inspec_property = #{property} SKIP"
94
- end
95
- end
96
- else
97
- Iggy::Log.debug "CloudFormation.parse_generate cfn_res_type = #{cfn_res_type} SKIP"
98
- end
99
- end
100
- generated_controls
101
- end
102
-
103
- end
104
- end
@@ -1,146 +0,0 @@
1
- #
2
- # Author:: Matt Ray (<matt@chef.io>)
3
- #
4
- # Copyright:: 2018, Chef Software, Inc <legal@chef.io>
5
- #
6
-
7
- require "inspec/objects/control"
8
- require "inspec/objects/ruby_helper"
9
- require "inspec/objects/describe"
10
-
11
- require "inspec-iggy/inspec_helper"
12
-
13
- module Iggy
14
- class Terraform
15
-
16
- # makes it easier to change out later
17
- TAG_NAME = "iggy_name_"
18
- TAG_URL = "iggy_url_"
19
-
20
- # boilerplate tfstate parsing
21
- def self.parse_tfstate(file)
22
- Inspec::Log.debug "Iggy::Terraform.parse_tfstate file = #{file}"
23
- begin
24
- unless File.file?(file)
25
- STDERR.puts "ERROR: #{file} is an invalid file, please check your path."
26
- exit(-1)
27
- end
28
- tfstate = JSON.parse(File.read(file))
29
- rescue JSON::ParserError => e
30
- STDERR.puts e.message
31
- STDERR.puts "ERROR: Parsing error in #{file}."
32
- exit(-1)
33
- end
34
- end
35
-
36
- # parse through the JSON for the tagged Resources
37
- def self.parse_extract(file)
38
- tfstate = parse_tfstate(file)
39
- # InSpec profiles extracted
40
- extracted_profiles = {}
41
-
42
- # iterate over the resources
43
- tf_resources = tfstate["modules"][0]["resources"]
44
- tf_resources.keys.each do |tf_res|
45
- tf_res_id = tf_resources[tf_res]["primary"]["id"]
46
-
47
- # get the attributes, see if any of them have a tagged profile attached
48
- tf_resources[tf_res]["primary"]["attributes"].keys.each do |attr|
49
- next unless attr.start_with?("tags." + TAG_NAME)
50
- Inspec::Log.debug "Iggy::Terraform.parse_extract tf_res = #{tf_res} attr = #{attr} MATCHED TAG"
51
- # get the URL and the name of the profiles
52
- name = attr.split(TAG_NAME)[1]
53
- url = tf_resources[tf_res]["primary"]["attributes"]["tags.#{TAG_URL}#{name}"]
54
- if tf_res.start_with?("aws_vpc") # should this be VPC or subnet?
55
- # if it's a VPC, store it as the VPC id + name
56
- key = tf_res_id + ":" + name
57
- Inspec::Log.debug "Iggy::Terraform.parse_extract aws_vpc tagged with InSpec #{key}"
58
- extracted_profiles[key] = {
59
- "type" => "aws_vpc",
60
- "az" => "us-west-2",
61
- "url" => url,
62
- }
63
- elsif tf_res.start_with?("aws_instance")
64
- # if it's a node, get information about the IP and SSH/WinRM
65
- key = tf_res_id + ":" + name
66
- Inspec::Log.debug "Iggy::Terraform.parse_extract aws_instance tagged with InSpec #{key}"
67
- extracted_profiles[key] = {
68
- "type" => "aws_instance",
69
- "public_ip" => tf_resources[tf_res]["primary"]["attributes"]["public_ip"],
70
- "key_name" => tf_resources[tf_res]["primary"]["attributes"]["key_name"],
71
- "url" => url,
72
- }
73
- else
74
- # should generic AWS just be the default except for instances?
75
- STDERR.puts "ERROR: #{file} #{tf_res_id} has an InSpec-tagged resource but #{tf_res} is currently unsupported."
76
- exit(-1)
77
- end
78
- end
79
- end
80
- Inspec::Log.debug "Iggy::Terraform.parse_extract extracted_profiles = #{extracted_profiles}"
81
- extracted_profiles
82
- end
83
-
84
- # parse through the JSON and generate InSpec controls
85
- def self.parse_generate(file)
86
- tfstate = parse_tfstate(file)
87
- basename = File.basename(file)
88
- absolutename = File.absolute_path(file)
89
-
90
- # InSpec controls generated
91
- generated_controls = []
92
-
93
- # iterate over the resources
94
- tf_resources = tfstate["modules"][0]["resources"]
95
- tf_resources.keys.each do |tf_res|
96
- tf_res_type = tf_resources[tf_res]["type"]
97
-
98
- # add translation layer
99
- if InspecHelper::TERRAFORM_RESOURCES.keys.include?(tf_res_type)
100
- Inspec::Log.debug "Iggy::Terraform.parse_generate tf_res_type = #{tf_res_type} #{InspecHelper::TERRAFORM_RESOURCES[tf_res_type]} TRANSLATED"
101
- tf_res_type = InspecHelper::TERRAFORM_RESOURCES[tf_res_type]
102
- end
103
-
104
- # does this match an InSpec resource?
105
- if InspecHelper::RESOURCES.include?(tf_res_type)
106
- Inspec::Log.debug "Iggy::Terraform.parse_generate tf_res_type = #{tf_res_type} MATCH"
107
- tf_res_id = tf_resources[tf_res]["primary"]["id"]
108
-
109
- # insert new control based off the resource's ID
110
- ctrl = Inspec::Control.new
111
- ctrl.id = "#{tf_res_type}::#{tf_res_id}"
112
- ctrl.title = "Iggy #{basename} #{tf_res_type}::#{tf_res_id}"
113
- ctrl.desc = "#{tf_res_type}::#{tf_res_id} from the source file #{absolutename}\nGenerated by Iggy v#{Iggy::VERSION}"
114
- ctrl.impact = "1.0"
115
-
116
- describe = Inspec::Describe.new
117
- # describes the resourde with the id as argument
118
- describe.qualifier.push([tf_res_type, tf_res_id])
119
-
120
- # ensure the resource exists
121
- describe.add_test(nil, "exist", nil)
122
-
123
- # if there's a match, see if there are matching InSpec properties
124
- inspec_properties = Iggy::InspecHelper.resource_properties(tf_res_type)
125
- tf_resources[tf_res]["primary"]["attributes"].keys.each do |attr|
126
- if inspec_properties.member?(attr)
127
- Inspec::Log.debug "Iggy::Terraform.parse_generate #{tf_res_type} inspec_property = #{attr} MATCH"
128
- value = tf_resources[tf_res]["primary"]["attributes"][attr]
129
- describe.add_test(attr, "cmp", value)
130
- else
131
- Inspec::Log.debug "Iggy::Terraform.parse_generate #{tf_res_type} inspec_property = #{attr} SKIP"
132
- end
133
- end
134
-
135
- ctrl.add_test(describe)
136
- generated_controls.push(ctrl)
137
- else
138
- Inspec::Log.debug "Iggy::Terraform.parse_generate tf_res_type = #{tf_res_type} SKIP"
139
- end
140
- end
141
- Inspec::Log.debug "Iggy::Terraform.parse_generate generated_controls = #{generated_controls}"
142
- generated_controls
143
- end
144
-
145
- end
146
- end
@@ -1,6 +0,0 @@
1
- {
2
- this
3
- file
4
- is
5
- junk
6
- }
@@ -1,851 +0,0 @@
1
-
2
- {
3
- "AWSTemplateFormatVersion": "2010-09-09",
4
- "Description": "BJC Chef Demo (4.5.4)",
5
- "Parameters": {
6
- "AvailabilityZone": {
7
- "Description": "Availability Zone",
8
- "Type": "String",
9
- "Default": "us-west-2c"
10
- },
11
- "DemoName": {
12
- "Description": "Name of the customer or organization",
13
- "Type": "String",
14
- "Default": "bjc-demo"
15
- },
16
- "Version": {
17
- "Description": "Version",
18
- "Type": "String",
19
- "Default": "4.5.4"
20
- },
21
- "KeyName": {
22
- "Description": "Name of an existing ec2 KeyPair to enable SSH access",
23
- "Type": "AWS::EC2::KeyPair::KeyName",
24
- "ConstraintDescription": "must be the name of an existing EC2 KeyPair."
25
- },
26
- "SSHLocation": {
27
- "Description": "The IP address range that can be used to SSH to the EC2 instances",
28
- "Type": "String",
29
- "MinLength": "9",
30
- "MaxLength": "18",
31
- "Default": "0.0.0.0/0",
32
- "AllowedPattern": "(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})/(\\d{1,2})",
33
- "ConstraintDescription": "must be a valid IP CIDR range of the form x.x.x.x/x."
34
- },
35
- "TTL": {
36
- "Description": "Time in hours for the demo to stay active. Default is 4, maximum is 720 hours (30 days).",
37
- "Type": "Number",
38
- "Default": 8,
39
- "MinValue": 0,
40
- "MaxValue": 720
41
- },
42
- "ChefServerAMI": {
43
- "Type": "String",
44
- "Default": "ami-3e6f1a46",
45
- "Description": "AMI ID for the Chef Server"
46
- },
47
- "BuildNode1AMI": {
48
- "Type": "String",
49
- "Default": "ami-cb6f1ab3",
50
- "Description": "AMI ID for Build Node 1"
51
- },
52
- "BuildNode2AMI": {
53
- "Type": "String",
54
- "Default": "ami-3c6d1844",
55
- "Description": "AMI ID for Build Node 2"
56
- },
57
- "BuildNode3AMI": {
58
- "Type": "String",
59
- "Default": "ami-4b6f1a33",
60
- "Description": "AMI ID for Build Node 3"
61
- },
62
- "deliveredAMI": {
63
- "Type": "String",
64
- "Default": "ami-676f1a1f",
65
- "Description": "AMI ID for delivered"
66
- },
67
- "ecomacceptanceAMI": {
68
- "Type": "String",
69
- "Default": "ami-de6c19a6",
70
- "Description": "AMI ID for ecomacceptance"
71
- },
72
- "rehearsalAMI": {
73
- "Type": "String",
74
- "Default": "ami-846c19fc",
75
- "Description": "AMI ID for rehearsal"
76
- },
77
- "unionAMI": {
78
- "Type": "String",
79
- "Default": "ami-1f621767",
80
- "Description": "AMI ID for union"
81
- },
82
- "WindowsWorkstation1AMI": {
83
- "Type": "String",
84
- "Default": "ami-ba6e1bc2",
85
- "Description": "AMI ID for the Windows Workstation"
86
- },
87
- "AutomateAMI": {
88
- "Type": "String",
89
- "Default": "ami-7d6c1905",
90
- "Description": "AMI ID for the Automate Server"
91
- }
92
- },
93
- "Resources": {
94
- "InstanceProfile" : {
95
- "Type" : "AWS::IAM::InstanceProfile",
96
- "Properties" : {
97
- "Path" : "/",
98
- "Roles" : ["chefDemo"]
99
- }
100
- },
101
- "VPC": {
102
- "Type": "AWS::EC2::VPC",
103
- "Properties": {
104
- "CidrBlock": "172.31.0.0/16",
105
- "EnableDnsSupport": "true",
106
- "EnableDnsHostnames": "true",
107
- "Tags": [
108
- {
109
- "Key": "Application",
110
- "Value": {
111
- "Ref": "AWS::StackId"
112
- },
113
- "Key": "Name",
114
- "Value": {
115
- "Fn::Join" : [ " ", [ { "Ref": "DemoName" }, "VPC" ] ]
116
- }
117
- }
118
- ]
119
- }
120
- },
121
- "SubnetAutomate": {
122
- "Type": "AWS::EC2::Subnet",
123
- "Properties": {
124
- "AvailabilityZone": { "Ref": "AvailabilityZone" },
125
- "VpcId": {
126
- "Ref": "VPC"
127
- },
128
- "CidrBlock": "172.31.54.0/24",
129
- "Tags": [
130
- {
131
- "Key": "Application",
132
- "Value": {
133
- "Ref": "AWS::StackId"
134
- },
135
- "Key": "Name",
136
- "Value": {
137
- "Fn::Join" : [ " ", [ { "Ref": "DemoName" }, "Automate Subnet" ] ]
138
- }
139
- }
140
- ]
141
- }
142
- },
143
- "SubnetProd": {
144
- "Type": "AWS::EC2::Subnet",
145
- "Properties": {
146
- "AvailabilityZone": { "Ref": "AvailabilityZone" },
147
- "VpcId": {
148
- "Ref": "VPC"
149
- },
150
- "CidrBlock": "172.31.62.0/24",
151
- "Tags": [
152
- {
153
- "Key": "Application",
154
- "Value": {
155
- "Ref": "AWS::StackId"
156
- },
157
- "Key": "Name",
158
- "Value": {
159
- "Fn::Join" : [ " ", [ { "Ref": "DemoName" }, "Prod Subnet" ] ]
160
- }
161
- }
162
- ]
163
- }
164
- },
165
- "SubnetWorkstations": {
166
- "Type": "AWS::EC2::Subnet",
167
- "Properties": {
168
- "AvailabilityZone": { "Ref": "AvailabilityZone" },
169
- "VpcId": {
170
- "Ref": "VPC"
171
- },
172
- "CidrBlock": "172.31.10.0/24",
173
- "Tags": [
174
- {
175
- "Key": "Application",
176
- "Value": {
177
- "Ref": "AWS::StackId"
178
- },
179
- "Key": "Name",
180
- "Value": {
181
- "Fn::Join" : [ " ", [ { "Ref": "DemoName" }, "Workstations Subnet" ] ]
182
- }
183
- }
184
- ]
185
- }
186
- },
187
- "InternetGateway": {
188
- "Type": "AWS::EC2::InternetGateway",
189
- "Properties": {
190
- "Tags": [
191
- {
192
- "Key": "Application",
193
- "Value": {
194
- "Ref": "AWS::StackId"
195
- },
196
- "Key": "Name",
197
- "Value": {
198
- "Fn::Join" : [ " ", [ { "Ref": "DemoName" }, " IG" ] ]
199
- }
200
- }
201
- ]
202
- }
203
- },
204
- "AttachGateway": {
205
- "Type": "AWS::EC2::VPCGatewayAttachment",
206
- "Properties": {
207
- "VpcId": {
208
- "Ref": "VPC"
209
- },
210
- "InternetGatewayId": {
211
- "Ref": "InternetGateway"
212
- }
213
- }
214
- },
215
- "RouteTable": {
216
- "Type": "AWS::EC2::RouteTable",
217
- "Properties": {
218
- "VpcId": {
219
- "Ref": "VPC"
220
- },
221
- "Tags": [
222
- {
223
- "Key": "Application",
224
- "Value": {
225
- "Ref": "AWS::StackId"
226
- },
227
- "Key": "Name",
228
- "Value": {
229
- "Fn::Join" : [ " ", [ { "Ref": "DemoName" }, "Demo RouteTable" ] ]
230
- }
231
- }
232
- ]
233
- }
234
- },
235
- "Route": {
236
- "Type": "AWS::EC2::Route",
237
- "DependsOn": "AttachGateway",
238
- "Properties": {
239
- "RouteTableId": {
240
- "Ref": "RouteTable"
241
- },
242
- "DestinationCidrBlock": "0.0.0.0/0",
243
- "GatewayId": {
244
- "Ref": "InternetGateway"
245
- }
246
- }
247
- },
248
- "SubnetRouteTableAssociationAutomate": {
249
- "Type": "AWS::EC2::SubnetRouteTableAssociation",
250
- "Properties": {
251
- "SubnetId": {
252
- "Ref": "SubnetAutomate"
253
- },
254
- "RouteTableId": {
255
- "Ref": "RouteTable"
256
- }
257
- }
258
- },
259
- "SubnetRouteTableAssociationProd": {
260
- "Type": "AWS::EC2::SubnetRouteTableAssociation",
261
- "Properties": {
262
- "SubnetId": {
263
- "Ref": "SubnetProd"
264
- },
265
- "RouteTableId": {
266
- "Ref": "RouteTable"
267
- }
268
- }
269
- },
270
- "SubnetRouteTableAssociationWorkstations": {
271
- "Type": "AWS::EC2::SubnetRouteTableAssociation",
272
- "Properties": {
273
- "SubnetId": {
274
- "Ref": "SubnetWorkstations"
275
- },
276
- "RouteTableId": {
277
- "Ref": "RouteTable"
278
- }
279
- }
280
- },
281
- "NetworkAcl": {
282
- "Type": "AWS::EC2::NetworkAcl",
283
- "Properties": {
284
- "VpcId": {
285
- "Ref": "VPC"
286
- },
287
- "Tags": [
288
- {
289
- "Key": "Application",
290
- "Value": {
291
- "Ref": "AWS::StackId"
292
- },
293
- "Key": "Name",
294
- "Value": {
295
- "Fn::Join" : [ " ", [ { "Ref": "DemoName" }, "NetworkAcl" ] ]
296
- }
297
- }
298
- ]
299
- }
300
- },
301
- "InboundNetworkAclEntry": {
302
- "Type": "AWS::EC2::NetworkAclEntry",
303
- "Properties": {
304
- "NetworkAclId": {
305
- "Ref": "NetworkAcl"
306
- },
307
- "RuleNumber": "100",
308
- "Protocol": "-1",
309
- "RuleAction": "allow",
310
- "Egress": "false",
311
- "CidrBlock": "0.0.0.0/0"
312
- }
313
- },
314
- "OutBoundNetworkAclEntry": {
315
- "Type": "AWS::EC2::NetworkAclEntry",
316
- "Properties": {
317
- "NetworkAclId": {
318
- "Ref": "NetworkAcl"
319
- },
320
- "RuleNumber": "100",
321
- "Protocol": "-1",
322
- "RuleAction": "allow",
323
- "Egress": "true",
324
- "CidrBlock": "0.0.0.0/0"
325
- }
326
- },
327
- "SubnetNetworkAclAssociationAutomate": {
328
- "Type": "AWS::EC2::SubnetNetworkAclAssociation",
329
- "Properties": {
330
- "SubnetId": {
331
- "Ref": "SubnetAutomate"
332
- },
333
- "NetworkAclId": {
334
- "Ref": "NetworkAcl"
335
- }
336
- }
337
- },
338
- "SubnetNetworkAclAssociationProd": {
339
- "Type": "AWS::EC2::SubnetNetworkAclAssociation",
340
- "Properties": {
341
- "SubnetId": {
342
- "Ref": "SubnetProd"
343
- },
344
- "NetworkAclId": {
345
- "Ref": "NetworkAcl"
346
- }
347
- }
348
- },
349
- "SubnetNetworkAclAssociationPOCWorkstations": {
350
- "Type": "AWS::EC2::SubnetNetworkAclAssociation",
351
- "Properties": {
352
- "SubnetId": {
353
- "Ref": "SubnetWorkstations"
354
- },
355
- "NetworkAclId": {
356
- "Ref": "NetworkAcl"
357
- }
358
- }
359
- },
360
- "WindowsWorkstation1": {
361
- "Type": "AWS::EC2::Instance",
362
- "Properties": {
363
- "InstanceType": "c4.large",
364
- "EbsOptimized" : "true",
365
- "IamInstanceProfile" : {"Ref" : "InstanceProfile"},
366
- "AvailabilityZone": { "Ref": "AvailabilityZone" },
367
- "NetworkInterfaces": [
368
- {
369
- "GroupSet": [
370
- {
371
- "Ref": "DemoSecurityGroup"
372
- }
373
- ],
374
- "AssociatePublicIpAddress": "true",
375
- "PrivateIpAddress": "172.31.54.201",
376
- "DeviceIndex": "0",
377
- "DeleteOnTermination": "true",
378
- "SubnetId": {
379
- "Ref": "SubnetAutomate"
380
- }
381
- }
382
- ],
383
- "KeyName": {
384
- "Ref": "KeyName"
385
- },
386
- "UserData" : {
387
- "Fn::Base64" : {
388
- "Fn::Join" : [
389
- "",
390
- ["<powershell>\n",
391
- "set-executionpolicy -executionpolicy unrestricted -force -scope LocalMachine",
392
- "</powershell>"
393
- ]
394
- ]
395
- }
396
- },
397
- "ImageId": {
398
- "Ref": "WindowsWorkstation1AMI"
399
- },
400
- "Tags": [
401
- {
402
- "Key": "Name",
403
- "Value": {
404
- "Fn::Join" : [ " ", [ { "Ref": "DemoName" }, "Workstation" ] ]
405
- }
406
- }
407
- ]
408
- }
409
- },
410
- "BuildNode1": {
411
- "Type": "AWS::EC2::Instance",
412
- "Properties": {
413
- "InstanceType": "m4.large",
414
- "AvailabilityZone": { "Ref": "AvailabilityZone" },
415
- "NetworkInterfaces": [
416
- {
417
- "GroupSet": [
418
- {
419
- "Ref": "DemoSecurityGroup"
420
- }
421
- ],
422
- "AssociatePublicIpAddress": "true",
423
- "PrivateIpAddress": "172.31.54.51",
424
- "DeviceIndex": "0",
425
- "DeleteOnTermination": "true",
426
- "SubnetId": {
427
- "Ref": "SubnetAutomate"
428
- }
429
- }
430
- ],
431
- "KeyName": { "Ref": "KeyName" },
432
- "UserData": { "Fn::Base64" : { "Fn::Join" : ["", [
433
- "#!/bin/bash -xe\n",
434
- "hostnamectl set-hostname build-node-1\n",
435
- "sleep 90\n",
436
- "sudo chef-client\n"]]}
437
- },
438
- "ImageId": {
439
- "Ref": "BuildNode1AMI"
440
- },
441
- "Tags": [
442
- {
443
- "Key": "Name",
444
- "Value": {
445
- "Fn::Join" : [ " ", [ { "Ref": "DemoName" }, "Build Node 1" ] ]
446
- }
447
- }
448
- ]
449
- }
450
- },
451
- "BuildNode2": {
452
- "Type": "AWS::EC2::Instance",
453
- "Properties": {
454
- "InstanceType": "m4.large",
455
- "AvailabilityZone": { "Ref": "AvailabilityZone" },
456
- "NetworkInterfaces": [
457
- {
458
- "GroupSet": [
459
- {
460
- "Ref": "DemoSecurityGroup"
461
- }
462
- ],
463
- "AssociatePublicIpAddress": "true",
464
- "PrivateIpAddress": "172.31.54.52",
465
- "DeviceIndex": "0",
466
- "DeleteOnTermination": "true",
467
- "SubnetId": {
468
- "Ref": "SubnetAutomate"
469
- }
470
- }
471
- ],
472
- "KeyName": { "Ref": "KeyName" },
473
- "UserData": { "Fn::Base64" : { "Fn::Join" : ["", [
474
- "#!/bin/bash -xe\n",
475
- "hostnamectl set-hostname build-node-2\n",
476
- "sleep 90\n",
477
- "sudo chef-client\n"]]}
478
- },
479
- "ImageId": {
480
- "Ref": "BuildNode2AMI"
481
- },
482
- "Tags": [
483
- {
484
- "Key": "Name",
485
- "Value": {
486
- "Fn::Join" : [ " ", [ { "Ref": "DemoName" }, "Build Node 2" ] ]
487
- }
488
- }
489
- ]
490
- }
491
- },
492
- "BuildNode3": {
493
- "Type": "AWS::EC2::Instance",
494
- "Properties": {
495
- "InstanceType": "m4.large",
496
- "AvailabilityZone": { "Ref": "AvailabilityZone" },
497
- "NetworkInterfaces": [
498
- {
499
- "GroupSet": [
500
- {
501
- "Ref": "DemoSecurityGroup"
502
- }
503
- ],
504
- "AssociatePublicIpAddress": "true",
505
- "PrivateIpAddress": "172.31.54.53",
506
- "DeviceIndex": "0",
507
- "DeleteOnTermination": "true",
508
- "SubnetId": {
509
- "Ref": "SubnetAutomate"
510
- }
511
- }
512
- ],
513
- "KeyName": { "Ref": "KeyName" },
514
- "UserData": { "Fn::Base64" : { "Fn::Join" : ["", [
515
- "#!/bin/bash -xe\n",
516
- "hostnamectl set-hostname build-node-3\n",
517
- "sleep 90\n",
518
- "sudo chef-client\n"]]}
519
- },
520
- "ImageId": {
521
- "Ref": "BuildNode3AMI"
522
- },
523
- "Tags": [
524
- {
525
- "Key": "Name",
526
- "Value": {
527
- "Fn::Join" : [ " ", [ { "Ref": "DemoName" }, "Build Node 3" ] ]
528
- }
529
- }
530
- ]
531
- }
532
- },
533
- "delivered": {
534
- "Type": "AWS::EC2::Instance",
535
- "Properties": {
536
- "InstanceType": "m4.large",
537
- "AvailabilityZone": { "Ref": "AvailabilityZone" },
538
- "NetworkInterfaces": [
539
- {
540
- "GroupSet": [
541
- {
542
- "Ref": "DemoSecurityGroup"
543
- }
544
- ],
545
- "AssociatePublicIpAddress": "true",
546
- "PrivateIpAddress": "172.31.54.101",
547
- "DeviceIndex": "0",
548
- "DeleteOnTermination": "true",
549
- "SubnetId": {
550
- "Ref": "SubnetAutomate"
551
- }
552
- }
553
- ],
554
- "KeyName": { "Ref": "KeyName" },
555
- "UserData": { "Fn::Base64" : { "Fn::Join" : ["", [
556
- "#!/bin/bash -xe\n",
557
- "hostnamectl set-hostname delivered\n",
558
- "sleep 90\n",
559
- "sudo chef-client\n"
560
- ]]}
561
- },
562
- "ImageId": {
563
- "Ref": "deliveredAMI"
564
- },
565
- "Tags": [
566
- {
567
- "Key": "Name",
568
- "Value": {
569
- "Fn::Join" : [ " ", [ { "Ref": "DemoName" }, "delivered" ] ]
570
- }
571
- }
572
- ]
573
- }
574
- },
575
- "ecomacceptance": {
576
- "Type": "AWS::EC2::Instance",
577
- "Properties": {
578
- "InstanceType": "m4.large",
579
- "AvailabilityZone": { "Ref": "AvailabilityZone" },
580
- "NetworkInterfaces": [
581
- {
582
- "GroupSet": [
583
- {
584
- "Ref": "DemoSecurityGroup"
585
- }
586
- ],
587
- "AssociatePublicIpAddress": "true",
588
- "PrivateIpAddress": "172.31.54.102",
589
- "DeviceIndex": "0",
590
- "DeleteOnTermination": "true",
591
- "SubnetId": {
592
- "Ref": "SubnetAutomate"
593
- }
594
- }
595
- ],
596
- "KeyName": { "Ref": "KeyName" },
597
- "UserData": { "Fn::Base64" : { "Fn::Join" : ["", [
598
- "#!/bin/bash -xe\n",
599
- "hostnamectl set-hostname ecomacceptance\n",
600
- "sleep 90\n",
601
- "sudo chef-client\n"
602
- ]]}
603
- },
604
- "ImageId": {
605
- "Ref": "ecomacceptanceAMI"
606
- },
607
- "Tags": [
608
- {
609
- "Key": "Name",
610
- "Value": {
611
- "Fn::Join" : [ " ", [ { "Ref": "DemoName" }, "ecomacceptance" ] ]
612
- }
613
- }
614
- ]
615
- }
616
- },
617
- "rehearsal": {
618
- "Type": "AWS::EC2::Instance",
619
- "Properties": {
620
- "InstanceType": "m4.large",
621
- "AvailabilityZone": { "Ref": "AvailabilityZone" },
622
- "NetworkInterfaces": [
623
- {
624
- "GroupSet": [
625
- {
626
- "Ref": "DemoSecurityGroup"
627
- }
628
- ],
629
- "AssociatePublicIpAddress": "true",
630
- "PrivateIpAddress": "172.31.54.103",
631
- "DeviceIndex": "0",
632
- "DeleteOnTermination": "true",
633
- "SubnetId": {
634
- "Ref": "SubnetAutomate"
635
- }
636
- }
637
- ],
638
- "KeyName": { "Ref": "KeyName" },
639
- "UserData": { "Fn::Base64" : { "Fn::Join" : ["", [
640
- "#!/bin/bash -xe\n",
641
- "hostnamectl set-hostname rehearsal\n",
642
- "sleep 90\n",
643
- "sudo chef-client\n"
644
- ]]}
645
- },
646
- "ImageId": {
647
- "Ref": "rehearsalAMI"
648
- },
649
- "Tags": [
650
- {
651
- "Key": "Name",
652
- "Value": {
653
- "Fn::Join" : [ " ", [ { "Ref": "DemoName" }, "rehearsal" ] ]
654
- }
655
- }
656
- ]
657
- }
658
- },
659
- "union": {
660
- "Type": "AWS::EC2::Instance",
661
- "Properties": {
662
- "InstanceType": "m4.large",
663
- "AvailabilityZone": { "Ref": "AvailabilityZone" },
664
- "NetworkInterfaces": [
665
- {
666
- "GroupSet": [
667
- {
668
- "Ref": "DemoSecurityGroup"
669
- }
670
- ],
671
- "AssociatePublicIpAddress": "true",
672
- "PrivateIpAddress": "172.31.54.104",
673
- "DeviceIndex": "0",
674
- "DeleteOnTermination": "true",
675
- "SubnetId": {
676
- "Ref": "SubnetAutomate"
677
- }
678
- }
679
- ],
680
- "KeyName": { "Ref": "KeyName" },
681
- "UserData": { "Fn::Base64" : { "Fn::Join" : ["", [
682
- "#!/bin/bash -xe\n",
683
- "hostnamectl set-hostname union\n",
684
- "sleep 90\n",
685
- "sudo chef-client\n"
686
- ]]}
687
- },
688
- "ImageId": {
689
- "Ref": "unionAMI"
690
- },
691
- "Tags": [
692
- {
693
- "Key": "Name",
694
- "Value": {
695
- "Fn::Join" : [ " ", [ { "Ref": "DemoName" }, "union" ] ]
696
- }
697
- }
698
- ]
699
- }
700
- },
701
- "Chef": {
702
- "Type": "AWS::EC2::Instance",
703
- "Properties": {
704
- "InstanceType": "c4.xlarge",
705
- "AvailabilityZone": { "Ref": "AvailabilityZone" },
706
- "BlockDeviceMappings" : [
707
- {
708
- "DeviceName" : "/dev/sda1",
709
- "Ebs" : { "VolumeSize" : "50" }
710
- }
711
- ],
712
- "NetworkInterfaces": [
713
- {
714
- "GroupSet": [
715
- {
716
- "Ref": "DemoSecurityGroup"
717
- }
718
- ],
719
- "AssociatePublicIpAddress": "true",
720
- "PrivateIpAddress": "172.31.54.10",
721
- "DeviceIndex": "0",
722
- "DeleteOnTermination": "true",
723
- "SubnetId": {
724
- "Ref": "SubnetAutomate"
725
- }
726
- }
727
- ],
728
- "KeyName": { "Ref": "KeyName" },
729
- "UserData": { "Fn::Base64" : { "Fn::Join" : ["", [
730
- "#!/bin/bash -xe\n",
731
- "hostnamectl set-hostname chef\n",
732
- "chef-server-ctl reconfigure\n"]]}
733
- },
734
- "ImageId": {
735
- "Ref": "ChefServerAMI"
736
- },
737
- "Tags": [
738
- {
739
- "Key": "Name",
740
- "Value": {
741
- "Fn::Join" : [ " ", [ { "Ref": "DemoName" }, "Chef Server" ] ]
742
- }
743
- }
744
- ]
745
- }
746
- },
747
- "Automate": {
748
- "Type": "AWS::EC2::Instance",
749
- "Properties": {
750
- "InstanceType": "c4.xlarge",
751
- "AvailabilityZone": { "Ref": "AvailabilityZone" },
752
- "BlockDeviceMappings" : [
753
- {
754
- "DeviceName" : "/dev/sda1",
755
- "Ebs" : { "VolumeSize" : "50" }
756
- }
757
- ] ,
758
- "NetworkInterfaces": [
759
- {
760
- "GroupSet": [
761
- {
762
- "Ref": "DemoSecurityGroup"
763
- }
764
- ],
765
- "AssociatePublicIpAddress": "true",
766
- "PrivateIpAddress": "172.31.54.11",
767
- "DeviceIndex": "0",
768
- "DeleteOnTermination": "true",
769
- "SubnetId": {
770
- "Ref": "SubnetAutomate"
771
- }
772
- }
773
- ],
774
- "KeyName": { "Ref": "KeyName" },
775
- "UserData": { "Fn::Base64" : { "Fn::Join" : ["", [
776
- "#!/bin/bash -xe\n",
777
- "hostnamectl set-hostname automate\n",
778
- "delivery-ctl reconfigure\n"]]}
779
- },
780
- "ImageId": {
781
- "Ref": "AutomateAMI"
782
- },
783
- "Tags": [
784
- {
785
- "Key": "Name",
786
- "Value": {
787
- "Fn::Join" : [ " ", [ { "Ref": "DemoName" }, "Automate Server" ] ]
788
- }
789
- }
790
- ]
791
- }
792
- },
793
- "DemoSecurityGroup": {
794
- "Type": "AWS::EC2::SecurityGroup",
795
- "Properties": {
796
- "VpcId": {
797
- "Ref": "VPC"
798
- },
799
- "GroupDescription": "Enable required ports for Chef Server",
800
- "SecurityGroupIngress": [
801
- {
802
- "IpProtocol": "tcp",
803
- "FromPort": "22",
804
- "ToPort": "22",
805
- "CidrIp": {
806
- "Ref": "SSHLocation"
807
- }
808
- },
809
- {
810
- "IpProtocol": "tcp",
811
- "FromPort": "0",
812
- "ToPort": "65535",
813
- "CidrIp": "172.31.0.0/16"
814
- },
815
- {
816
- "IpProtocol": "tcp",
817
- "FromPort": "3389",
818
- "ToPort": "3389",
819
- "CidrIp": "0.0.0.0/0"
820
- },
821
- {
822
- "IpProtocol": "tcp",
823
- "FromPort": "443",
824
- "ToPort": "443",
825
- "CidrIp": "0.0.0.0/0"
826
- },
827
- {
828
- "IpProtocol": "icmp",
829
- "FromPort": "8",
830
- "ToPort": "-1",
831
- "CidrIp": "0.0.0.0/0"
832
- },
833
- {
834
- "IpProtocol": "udp",
835
- "FromPort": "3389",
836
- "ToPort": "3389",
837
- "CidrIp": "0.0.0.0/0"
838
- },
839
- {
840
- "IpProtocol": "tcp",
841
- "FromPort": "5985",
842
- "ToPort": "5985",
843
- "CidrIp": "0.0.0.0/0"
844
- }
845
- ]
846
- }
847
- }
848
- },
849
- "Outputs":
850
- {"WindowsWorkstation1PubDNS":{"Description":"Public IP address of the Windows Workstation","Value":{"Fn::GetAtt":["WindowsWorkstation1","PublicIp"]}}}
851
- }