terraorg 0.2.3 → 0.5.3

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 60e495604f197ced6264c00cea8607d7c846fd65179e024eaeabcf4ea3e28fb7
4
- data.tar.gz: b24f650f9f48b5707b10f2cf4422e7cca438f257a57b0f5276abb90d34f5115c
3
+ metadata.gz: '081a5e9b4201f219e7b153a554a57e32060a8c043b2769d8bd9199d25f871850'
4
+ data.tar.gz: f6be12775e8435085b35da3bdc0b66af701705ffc9c6d0de45a6cc47cc237737
5
5
  SHA512:
6
- metadata.gz: 634029af22269e47401fa78d47f11aa34dbe0cff1343459db6d344a9c496a02ba9d2ed97c3d2bae5dc95a4a01e44cbdb794160459a7ffb23d33c9c360f96acd0
7
- data.tar.gz: 51d2f8ddebbe751e2e935ce30d68c111b090a500fa35edf61f4ed9011d63ccb388f2d4e04b719c2eb31b6012f871cafba4088bbab3eb9f97662d3751e62ccc39
6
+ metadata.gz: 8ca99240c4e75a63c6af0e7ff83efe3994dc3da8ac3072a524d3c845a5c99f68211bc189aafe53ed03593a9c748d7179d5f92bc67b4c15cbdc33c1ca3c632c71
7
+ data.tar.gz: dcb7597efd163ca22e17968c81cd80dd599ddaa98acf304972e25ed5fafe6c9745f15ec143878c6f3898ed415393caaf109b25d5312953fc9fe6393936b0a0db
data/README.md CHANGED
@@ -34,7 +34,9 @@ Based on the org that this tool was originally designed for, orgs are expected
34
34
  to have three levels:
35
35
 
36
36
  * *squads*: the base unit of team-dom, containing people, who may be in
37
- different geographical regions.
37
+ different geographical regions. Teams contain _members_ (full time heads)
38
+ and _associates_ (typically part time floaters.) Any associate of a squad
39
+ must also have a home squad for which they are a full time member.
38
40
  * *platoons*: a unit which contains squads and exceptional people who are
39
41
  members of the platoon, but not part of any squad
40
42
  * *org*: The whole organization, including its manager, any exceptional squads
@@ -45,6 +47,10 @@ The tool generates groups for each granular unit of organization in Okta and G
45
47
  Suite in Terraform. With patching, it could be possible for more organizational
46
48
  systems to be supported.
47
49
 
50
+ ## Diagram
51
+
52
+ ![Diagram of org structure](img/diagram.png)
53
+
48
54
  ## How it works
49
55
 
50
56
  Firstly, take your entire existing organization and define it using the
@@ -120,6 +126,10 @@ information on how to configure the providers.
120
126
  [articulate/terraform-provider-okta]: https://github.com/articulate/terraform-provider-okta
121
127
  [DeviaVir/terraform-provider-gsuite]: https://github.com/DeviaVir/terraform-provider-gsuite
122
128
 
129
+ ## Running tests
130
+ There are a limited number of tests that can be invoked with
131
+ `ruby -I lib test/terraorg/model/org_test.rb `
132
+
123
133
  ## Suggested process
124
134
 
125
135
  At [LiveRamp], a pull request based workflow leveraging [Atlantis] is used to
@@ -31,6 +31,8 @@ ACTIONS = [
31
31
  'validate'
32
32
  ].freeze
33
33
 
34
+ STRICT_VALIDATION = ENV.fetch('TERRAORG_STRICT_VALIDATION', 'true')
35
+ ALLOW_ORPHANED_ASSOCIATES = ENV.fetch('ALLOW_ORPHANED_ASSOCIATES', 'false')
34
36
  SQUADS_FILE = ENV.fetch('TERRAORG_SQUADS', 'squads.json')
35
37
  PLATOONS_FILE = ENV.fetch('TERRAORG_PLATOONS', 'platoons.json')
36
38
  ORG_FILE = ENV.fetch('TERRAORG_ROOT', 'org.json')
@@ -95,7 +97,9 @@ platoons = Platoons.new(JSON.parse(platoons_data), squads, people, GSUITE_DOMAIN
95
97
  org_data = File.read(ORG_FILE)
96
98
  org = Org.new(JSON.parse(org_data), platoons, squads, people, GSUITE_DOMAIN)
97
99
 
98
- org.validate!
100
+ strict = (STRICT_VALIDATION == 'true')
101
+ allow_orphaned_associates = (ALLOW_ORPHANED_ASSOCIATES == 'true')
102
+ org.validate!(strict: strict, allow_orphaned_associates: allow_orphaned_associates)
99
103
 
100
104
  case action
101
105
  when 'generate-squads-md'
@@ -1,4 +1,5 @@
1
1
  # Copyright 2019-2020 LiveRamp Holdings, Inc.
2
+ # Copyright 2020- Joshua Kwan
2
3
  #
3
4
  # Licensed under the Apache License, Version 2.0 (the "License");
4
5
  # you may not use this file except in compliance with the License.
@@ -49,23 +50,33 @@ class Org
49
50
  @squads = squads
50
51
  end
51
52
 
52
- def validate!
53
+ def validate!(strict: true, allow_orphaned_associates: false)
54
+ failure = false
55
+
53
56
  # Do not allow the JSON files to contain any people who have left.
54
- raise "Users have left the company: #{@people.inactive.map(&:id).join(', ')}" unless @people.inactive.empty?
57
+ unless @people.inactive.empty?
58
+ $stderr.puts "ERROR: Users have left the company, or are Suspended in Okta: #{@people.inactive.map(&:id).join(', ')}"
59
+ failure = true
60
+ end
55
61
 
56
62
  # Do not allow the org to be totally empty.
57
- raise 'Org has no platoons or exception squads' if @member_platoons.size + @member_exception_squads.size == 0
63
+ if @member_platoons.size + @member_exception_squads.size == 0
64
+ $stderr.puts 'ERROR: Org has no platoons or exception squads'
65
+ failure = true
66
+ end
58
67
 
59
68
  # Require all platoons to be part of the org.
60
69
  platoon_diff = Set.new(@platoons.all_names) - Set.new(@member_platoon_names)
61
70
  unless platoon_diff.empty?
62
- raise "Platoons are not used in the org: #{platoon_diff.to_a.sort}"
71
+ $stderr.puts "ERROR: Platoons are not used in the org: #{platoon_diff.to_a.sort}"
72
+ failure = true
63
73
  end
64
74
 
65
75
  # Require all squads to be used in the org.
66
76
  squad_diff = Set.new(@squads.all_names) - Set.new(@platoons.all_squad_names) - Set.new(@member_exception_squad_names)
67
77
  unless squad_diff.empty?
68
- raise "Squad(s) are not used in the org: #{squad_diff.to_a.sort}"
78
+ $stderr.puts "ERROR: Squad(s) are not used in the org: #{squad_diff.to_a.sort}"
79
+ failure = true
69
80
  end
70
81
 
71
82
  all_squads = (@member_platoons.map(&:member_squads) + @member_exception_squads).flatten
@@ -79,34 +90,37 @@ class Org
79
90
  count > 1
80
91
  end
81
92
  if !more_than_one_platoon.empty?
82
- raise "Squads are part of more than one platoon: #{more_than_one_platoon}"
93
+ $stderr.puts "ERROR: Squads are part of more than one platoon: #{more_than_one_platoon}"
94
+ failure = true
83
95
  end
84
96
 
85
97
  # Validate that a squad member belongs to some maximum number of squads
86
98
  # across the entire org. A person can be an associate of other squads
87
99
  # at a different count. See top of file for defined limits.
88
100
  squad_count = {}
89
- all_squads.map(&:teams).flatten.map(&:values).flatten.map(&:members).flatten.each do |member|
101
+ all_members = all_squads.map(&:teams).flatten.map(&:values).flatten.map(&:members).flatten
102
+ all_members.each do |member|
90
103
  squad_count[member.id] = squad_count.fetch(member.id, 0) + 1
91
104
  end
92
105
  more_than_max_squads = squad_count.select do |member, count|
93
106
  count > MAX_MEMBER_SQUADS_PER_PERSON
94
107
  end
95
108
  if !more_than_max_squads.empty?
96
- # TODO(joshk): Enforce after April 17th
97
- $stderr.puts "WARNING: Members are part of more than #{MAX_MEMBER_SQUADS_PER_PERSON} squads: #{more_than_max_squads}"
109
+ $stderr.puts "ERROR: People are members of more than #{MAX_MEMBER_SQUADS_PER_PERSON} squads: #{more_than_max_squads}"
110
+ failure = true
98
111
  end
99
112
 
100
113
  associate_count = {}
101
- all_squads.map(&:teams).flatten.map(&:values).flatten.map(&:associates).flatten.each do |assoc|
114
+ all_associates = all_squads.map(&:teams).flatten.map(&:values).flatten.map(&:associates).flatten
115
+ all_associates.each do |assoc|
102
116
  associate_count[assoc.id] = associate_count.fetch(assoc.id, 0) + 1
103
117
  end
104
118
  more_than_max_squads = associate_count.select do |_, count|
105
119
  count > MAX_ASSOCIATE_SQUADS_PER_PERSON
106
120
  end
107
121
  if !more_than_max_squads.empty?
108
- # TODO(joshk): Enforce after April 17th
109
- $stderr.puts "WARNING: People associated with more than #{MAX_ASSOCIATE_SQUADS_PER_PERSON} squads: #{more_than_max_squads}"
122
+ $stderr.puts "ERROR: People are associates of more than #{MAX_ASSOCIATE_SQUADS_PER_PERSON} squads: #{more_than_max_squads}"
123
+ failure = true
110
124
  end
111
125
 
112
126
  # Validate that a squad member is not also an org exception
@@ -115,8 +129,20 @@ class Org
115
129
  exceptions.member? member
116
130
  end
117
131
  if !exception_and_squad_member.empty?
118
- raise "Exception members are also squad members: #{exception_and_squad_member}"
132
+ $stderr.puts "ERROR: Exception members are also squad members: #{exception_and_squad_member}"
133
+ failure = true
119
134
  end
135
+
136
+ # Validate that any associate is a member of some squad
137
+ if !allow_orphaned_associates
138
+ associates_but_not_members = Set.new(all_associates.map(&:id)) - Set.new(all_members.map(&:id)) - exceptions
139
+ if !associates_but_not_members.empty?
140
+ $stderr.puts "ERROR: #{associates_but_not_members.to_a} are associates of squads but not members of any squad"
141
+ failure = true
142
+ end
143
+ end
144
+
145
+ raise "CRITICAL: Validation failed due to at least one error above" if failure && strict
120
146
  end
121
147
 
122
148
  def members
@@ -179,13 +205,16 @@ class Org
179
205
  md_lines.join("\n")
180
206
  end
181
207
 
182
- def generate_tf
183
- tf = @member_platoons.map { |p| p.generate_tf(@id) }.join("\n")
184
- File.write('auto.platoons.tf', tf)
208
+ def generate_tf_platoons
209
+ @member_platoons.map { |p| p.generate_tf(@id) }.join("\n")
210
+ end
185
211
 
186
- tf = @member_exception_squads.map { |s| s.generate_tf(@id) }.join("\n")
187
- File.write('auto.exception_squads.tf', tf)
212
+ def generate_tf_squads
213
+ @member_exception_squads.map { |s| s.generate_tf(@id) }.join("\n")
214
+ end
188
215
 
216
+ def generate_tf_org
217
+ tf = ''
189
218
  # Roll all platoons and exception squads into the org.
190
219
  roll_up_to_org = \
191
220
  @member_exception_squads.map { |s| s.unique_name(@id, nil) } + \
@@ -225,14 +254,18 @@ EOF
225
254
  all_locations[@manager_location] = all_locations.fetch(@manager_location, Set.new).add(@manager)
226
255
 
227
256
  all_locations.each do |l, m|
257
+ description = "#{@name} organization members based in #{l} (terraorg)"
228
258
  name = "#{unique_name}-#{l.downcase}"
229
259
  tf += <<-EOF
230
260
  resource "okta_group" "#{name}" {
231
261
  name = "#{name}"
232
- description = "#{@name} organization members based in #{l} (terraorg)"
262
+ description = "#{description}"
233
263
  users = #{Util.persons_tf(m)}
234
264
  }
265
+
266
+ #{Util.gsuite_group_tf(name, @gsuite_domain, m, description)}
235
267
  EOF
268
+
236
269
  end
237
270
 
238
271
  # Generate a special GSuite group for all managers (org, platoon, squad
@@ -241,7 +274,17 @@ EOF
241
274
  all_managers = Set.new([@manager] + @platoons.all.map(&:manager) + @squads.all.map(&:manager).select { |m| m })
242
275
  manager_dl = "#{@id}-managers"
243
276
  tf += Util.gsuite_group_tf(manager_dl, @gsuite_domain, all_managers, "All managers of the #{@name} organization (terraorg)")
277
+ tf
278
+ end
279
+
280
+ def generate_tf
281
+ tf = generate_tf_platoons
282
+ File.write('auto.platoons.tf', tf)
283
+
284
+ tf = generate_tf_squads
285
+ File.write('auto.exception_squads.tf', tf)
244
286
 
287
+ tf = generate_tf_org
245
288
  File.write('auto.org.tf', tf)
246
289
  end
247
290
 
@@ -15,7 +15,7 @@
15
15
  require 'faraday'
16
16
 
17
17
  class Person
18
- ACTIVE_USER_STATUSES = ['ACTIVE', 'PROVISIONED'].freeze
18
+ ACTIVE_USER_STATUSES = ['ACTIVE', 'PROVISIONED', 'PASSWORD_EXPIRED'].freeze
19
19
 
20
20
  attr_accessor :id, :name, :okta_id, :email, :status
21
21
 
@@ -13,5 +13,5 @@
13
13
  # limitations under the License.
14
14
 
15
15
  module Terraorg
16
- VERSION = '0.2.3'
16
+ VERSION = '0.5.3'
17
17
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: terraorg
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.3
4
+ version: 0.5.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joshua Kwan
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-04-13 00:00:00.000000000 Z
11
+ date: 2021-01-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: countries
@@ -66,6 +66,20 @@ dependencies:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0.2'
69
+ - !ruby/object:Gem::Dependency
70
+ name: minitest
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '5.14'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '5.14'
69
83
  description: Manage an organizational structure with Okta and G-Suite using Terraform
70
84
  email: joshk@triplehelix.org
71
85
  executables:
@@ -104,7 +118,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
104
118
  - !ruby/object:Gem::Version
105
119
  version: '0'
106
120
  requirements: []
107
- rubygems_version: 3.0.3
121
+ rubygems_version: 3.0.8
108
122
  signing_key:
109
123
  specification_version: 4
110
124
  summary: terraorg