From befd1cc7f10edb94af65e397c7c4bfb0d75a4eb8 Mon Sep 17 00:00:00 2001 From: William Theaker Date: Tue, 21 Jun 2022 13:30:27 -0400 Subject: [PATCH 1/2] Add macOS MDM profiles and Windows support. --- chef/cookbooks/cpe_zoom/README.md | 21 ++-- chef/cookbooks/cpe_zoom/metadata.rb | 2 - chef/cookbooks/cpe_zoom/resources/cpe_zoom.rb | 109 ++++++++++++++++-- 3 files changed, 111 insertions(+), 21 deletions(-) diff --git a/chef/cookbooks/cpe_zoom/README.md b/chef/cookbooks/cpe_zoom/README.md index 0a3689a..1ed086a 100644 --- a/chef/cookbooks/cpe_zoom/README.md +++ b/chef/cookbooks/cpe_zoom/README.md @@ -1,6 +1,6 @@ cpe_zoom Cookbook ======================== -Install a profile to manage diagnostic information submission settings. +Manages Zoom Desktop Client configuration on macOS and Windows. Attributes @@ -9,18 +9,23 @@ Attributes Usage ----- -The profile will manage the `us.zoom.config` preference domain. +On macOS, a profile will manage the `us.zoom.config` preference domain. -The profile's organization key defaults to `Uber` unless `node['organization']` is +The profile's organization key defaults to `Uber`, unless `node['organization']` is configured in your company's custom init recipe. The profile will also use whichever prefix is set in node['cpe_profiles']['prefix'], which defaults to `com.facebook.chef` -The profile delivers a payload for the above keys in `node['cpe_zoom']`. The three provided have a sane default, which can be overridden in another recipe if desired. +The profile delivers a payload for the above keys in `node['cpe_zoom']`. The three provided have a sensible default, which can be overridden in another recipe if desired. -This cookbook provides zero keys within the default attributes as there are many undocumented keys. +This cookbook doesn't provide any keys within the default attributes as there are many undocumented keys. -For a list of supported keys, please see this [Zoom knowledge base article](https://support.zoom.us/hc/en-us/articles/115001799006-Mass-Deployment-with-Preconfigured-Settings-for-Mac) +On Windows, take care not to deploy Zoom with MSI install arguments which conflict with the registry keys you set via this cookbook. -For example, you could tweak the above values - # Disable the ability for Zoom to turn on your webcam when joining a meeting. +For a list of supported configuration keys, please see the Zoom knowledge base articles for [macOS](https://support.zoom.us/hc/en-us/articles/115001799006-Mass-Deployment-with-Preconfigured-Settings-for-Mac) and [Windows](https://support.zoom.us/hc/en-us/articles/201362163). + +Example +----- +```ruby + # Disable activating your webcam when joining meetings. node.default['cpe_zoom']['ZDisableVideo'] = true +``` diff --git a/chef/cookbooks/cpe_zoom/metadata.rb b/chef/cookbooks/cpe_zoom/metadata.rb index 79cccd9..8a751a0 100644 --- a/chef/cookbooks/cpe_zoom/metadata.rb +++ b/chef/cookbooks/cpe_zoom/metadata.rb @@ -7,5 +7,3 @@ chef_version '>= 14.14' depends 'cpe_profiles' -depends 'cpe_utils' -depends 'uber_helpers' diff --git a/chef/cookbooks/cpe_zoom/resources/cpe_zoom.rb b/chef/cookbooks/cpe_zoom/resources/cpe_zoom.rb index a1b9085..8eadc3b 100644 --- a/chef/cookbooks/cpe_zoom/resources/cpe_zoom.rb +++ b/chef/cookbooks/cpe_zoom/resources/cpe_zoom.rb @@ -2,8 +2,6 @@ # Cookbook:: cpe_zoom # Resources:: cpe_zoom # -# vim: syntax=ruby:expandtab:shiftwidth=2:softtabstop=2:tabstop=2 -# # Copyright:: (c) 2019-present, Uber Technologies, Inc. # All rights reserved. # @@ -17,21 +15,110 @@ default_action :run -# Enforce Zoom Settings -action :run do - zoom_prefs = node['cpe_zoom'].compact - unless zoom_prefs.empty? - zoom_prefs.each_key do |key| - next if zoom_prefs[key].nil? +action_class do + WINDOWS_GENERAL_SETTINGS_MAP = { + "ZAutoSSOLogin" => "ForceLoginWithSSO", + "ZSSOHost" => "ForceSSOURL", + "ZAutoUpdate" => "EnableClientAutoUpdate", + "disableloginwithemail" => "DisableLoginWithEmail", + "nofacebook" => "DisableFacebookLogin", + "nogoogle" => "DisableGoogleLogin", + }.freeze + + WINDOWS_CHAT_SETTINGS_MAP = { + "DisableLinkPreviewInChat" => "DisableLinkPreviewInChat", + }.freeze + + WINDOWS_IGNORABLE_SETTINGS_MAP = [ + "LastLoginType", + "forcessourl", # ZSSOHost is already mapped to ForceSSOURL + ].freeze + + def zoom_prefs + node["cpe_zoom"].compact + end + + def convert_to_registry_key(value) + if value == true + return { "data": 1, "type": :dword } + elsif value == false + return { "data": 0, "type": :dword } + else + return { "data": value.to_s, "type": :string } + end + end + + def configure_windows + zoom_settings = {} + zoom_prefs.each do |key, value| + next if WINDOWS_IGNORABLE_SETTINGS_MAP.include?(key) # Skippable preferences on Windows + + preference = WINDOWS_CHAT_SETTINGS_MAP[key] + if preference.nil? # If the preference isn't in WINDOWS_CHAT_SETTINGS_MAP, set General path. + zoom_settings[WINDOWS_GENERAL_SETTINGS_MAP.fetch(key, key)] = { "path": 'HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Zoom\Zoom Meetings\General', "name": WINDOWS_GENERAL_SETTINGS_MAP.fetch(key, key) }.merge(convert_to_registry_key(value)) + else + zoom_settings[preference] = { "path": 'HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Zoom\Zoom Meetings\chat', "name": key }.merge(convert_to_registry_key(value)) + end + end - # Zoom doesn't use profiles atm. Chef 14+ - if node.at_least_chef14? + unless zoom_settings.empty? + zoom_settings.each do | name, reg_key | + registry_key reg_key[:path] do + values [{ name: reg_key[:name], type: reg_key[:type], data: reg_key[:data] }] + recursive true + action :create + end + end + end + + end + + def configure_macos + prefix = node["cpe_profiles"]["prefix"] + organization = node["organization"] || "Uber" + zoom_profile = { + "PayloadIdentifier" => "#{prefix}.zoom", + "PayloadRemovalDisallowed" => true, + "PayloadScope" => "System", + "PayloadType" => "Configuration", + "PayloadUUID" => "B1B0DEED-DC7C-4122-912F-A22F660DF53D", + "PayloadOrganization" => organization, + "PayloadVersion" => 1, + "PayloadDisplayName" => "Zoom", + "PayloadContent" => [], + } + unless zoom_prefs.empty? + zoom_profile["PayloadContent"].push( + "PayloadType" => "us.zoom.config", + "PayloadVersion" => 1, + "PayloadIdentifier" => "#{prefix}.zoom", + "PayloadUUID" => "B976C3E1-B59D-4060-80DA-13A42270D1E7", + "PayloadEnabled" => true, + "PayloadDisplayName" => "Zoom", + ) + zoom_prefs.each_key do |key| + next if zoom_prefs[key].nil? + zoom_profile["PayloadContent"][0][key] = zoom_prefs[key] + # Double tap the preferences since Zoom didn't use profiles at one point. Requires Chef 14 or newer macos_userdefaults "Configure us.zoom.config - #{key}" do - domain '/Library/Preferences/us.zoom.config' + domain "/Library/Preferences/us.zoom.config" key key value zoom_prefs[key] end end end + + node.default["cpe_profiles"]["#{prefix}.zoom"] = zoom_profile + end +end + +# Enforce Zoom Settings +action :run do + return if zoom_prefs.empty? + + if macos? + configure_macos + elsif windows? + configure_windows end end From d58f788963dbab253be2fd002621ebf217d7fd29 Mon Sep 17 00:00:00 2001 From: William Theaker Date: Thu, 30 Jun 2022 12:01:29 -0400 Subject: [PATCH 2/2] Update rubocop rules and run cookstyle. --- .rubocop.yml | 149 ++++++++++-------- chef/cookbooks/cpe_zoom/resources/cpe_zoom.rb | 83 +++++----- opensource_links.md | 7 +- 3 files changed, 128 insertions(+), 111 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index f8ab3e1..342a28c 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,149 +1,160 @@ +require: + - rubocop-performance + # # Disabled rules # -Encoding: +## Layout +# Seems buggy - https://github.com/bbatsov/rubocop/issues/2690 +Layout/MultilineOperationIndentation: Enabled: false -NumericLiterals: +## Lint +# it wants File.exist? instead of File.exists? +Lint/DeprecatedClassMethods: Enabled: false -MultilineTernaryOperator: +## Metrics +Metrics/AbcSize: Enabled: false -ModuleLength: +# Unrealistic +Metrics/BlockNesting: Enabled: false -MethodLength: +Metrics/ClassLength: Enabled: false -ClassLength: +Metrics/CyclomaticComplexity: Enabled: false -CyclomaticComplexity: +Metrics/MethodLength: Enabled: false -# So just keep iterating instead of breaking? wtf. -Next: +Metrics/ModuleLength: Enabled: false -# While this can be nice, it also can promote errors. Let people -# use what's comfortable for them -GuardClause: +# I don't know what it's metric for "human complexity" is, but it's wrong. +Metrics/PerceivedComplexity: Enabled: false -AbcSize: +## Naming + +# this trips on *any* method called 'get_*' wtf. +Naming/AccessorMethodName: Enabled: false -# less readable, not more -IfUnlessModifier: +# This blows up on things like base_packages-redhat +Naming/FileName: Enabled: false -# Really? -PerlBackrefs: +## Performance +# Performance rules have moved to the rubocop-performance gem. + +# buggy: https://github.com/bbatsov/rubocop/issues/2639 +Performance/RedundantMatch: Enabled: false -# Unrealistic -BlockNesting: +# https://github.com/bbatsov/rubocop/issues/2676 +Performance/RedundantMerge: Enabled: false -# Disabled because of the way 'variables' works. -BracesAroundHashParameters: +# We'll .times.map all we want. +Performance/TimesMap: Enabled: false -WordArray: +## Style + +Style/CommentAnnotation: Enabled: false -RedundantReturn: +# Bug with constants? https://phabricator.fb.com/P56108678 +Style/ConditionalAssignment: Enabled: false -RedundantSelf: +Style/Documentation: Enabled: false -CommentAnnotation: +Style/Encoding: Enabled: false -# this trips on *any* method called 'get_*' wtf. -AccessorMethodName: +# No more 'Missing frozen string literal comment.' linting violations +Style/FrozenStringLiteralComment: Enabled: false -# backslash is extra dumb in ruby, we want the OPPOSITE of this rule -LineEndConcatenation: +# While this can be nice, it also can promote errors. Let people +# use what's comfortable for them +Style/GuardClause: Enabled: false -# this isn't testing for consistency it always wants %w() which is dumb -PercentLiteralDelimiters: +# less readable, not more +Style/IfUnlessModifier: Enabled: false -# it wants File.exist? instead of File.exists? -DeprecatedClassMethods: +# backslash is extra dumb in ruby, we want the OPPOSITE of this rule +Style/LineEndConcatenation: Enabled: false -# This blows up on things like base_packages-redhat -FileName: +Style/MultilineTernaryOperator: Enabled: false -# I don't know what it's metric for "human complexity" is, but it's wrong. -PerceivedComplexity: +# We don't use CAPS just for constants. +Style/MutableConstant: Enabled: false -# Seems buggy - https://github.com/bbatsov/rubocop/issues/2690 -MultilineOperationIndentation: +# So just keep iterating instead of breaking? wtf. +Style/Next: Enabled: false -# buggy: https://github.com/bbatsov/rubocop/issues/2639 -Performance/RedundantMatch: +Style/NumericLiterals: Enabled: false -# We don't use CAPS just for constants. -Style/MutableConstant: +# this isn't testing for consistency it always wants %w() which is dumb +Style/PercentLiteralDelimiters: Enabled: false -# We'll .times.map all we want. -Performance/TimesMap: +# Really? +Style/PerlBackrefs: Enabled: false -# https://github.com/bbatsov/rubocop/issues/2676 -Performance/RedundantMerge: +Style/RedundantReturn: Enabled: false -# Bug with constants? https://phabricator.fb.com/P56108678 -Style/ConditionalAssignment: +Style/RedundantSelf: Enabled: false -# No more 'Missing frozen string literal comment.' linting violations -Style/FrozenStringLiteralComment: +Style/WordArray: Enabled: false + # # Modified rules # -LineLength: - Max: 120 -DotPosition: +Layout/DotPosition: EnforcedStyle: trailing -HashSyntax: +Layout/FirstArrayElementIndentation: + EnforcedStyle: consistent + +Layout/LineLength: + Max: 120 + +Metrics/BlockLength: + Max: 80 + +Style/HashSyntax: EnforcedStyle: hash_rockets -Style/Documentation: - Enabled: false +Style/SignalException: + EnforcedStyle: semantic -TrailingCommaInArrayLiteral: +Style/TrailingCommaInArguments: EnforcedStyleForMultiline: comma -TrailingCommaInHashLiteral: +Style/TrailingCommaInArrayLiteral: EnforcedStyleForMultiline: comma -TrailingCommaInArguments: +Style/TrailingCommaInHashLiteral: EnforcedStyleForMultiline: comma - -Layout/IndentArray: - EnforcedStyle: consistent - -Style/SignalException: - EnforcedStyle: semantic - -Metrics/BlockLength: - Max: 80 diff --git a/chef/cookbooks/cpe_zoom/resources/cpe_zoom.rb b/chef/cookbooks/cpe_zoom/resources/cpe_zoom.rb index 8eadc3b..ec4c187 100644 --- a/chef/cookbooks/cpe_zoom/resources/cpe_zoom.rb +++ b/chef/cookbooks/cpe_zoom/resources/cpe_zoom.rb @@ -17,34 +17,34 @@ action_class do WINDOWS_GENERAL_SETTINGS_MAP = { - "ZAutoSSOLogin" => "ForceLoginWithSSO", - "ZSSOHost" => "ForceSSOURL", - "ZAutoUpdate" => "EnableClientAutoUpdate", - "disableloginwithemail" => "DisableLoginWithEmail", - "nofacebook" => "DisableFacebookLogin", - "nogoogle" => "DisableGoogleLogin", + 'ZAutoSSOLogin' => 'ForceLoginWithSSO', + 'ZSSOHost' => 'ForceSSOURL', + 'ZAutoUpdate' => 'EnableClientAutoUpdate', + 'disableloginwithemail' => 'DisableLoginWithEmail', + 'nofacebook' => 'DisableFacebookLogin', + 'nogoogle' => 'DisableGoogleLogin', }.freeze WINDOWS_CHAT_SETTINGS_MAP = { - "DisableLinkPreviewInChat" => "DisableLinkPreviewInChat", + 'DisableLinkPreviewInChat' => 'DisableLinkPreviewInChat', }.freeze WINDOWS_IGNORABLE_SETTINGS_MAP = [ - "LastLoginType", - "forcessourl", # ZSSOHost is already mapped to ForceSSOURL + 'LastLoginType', + 'forcessourl', # ZSSOHost is already mapped to ForceSSOURL ].freeze def zoom_prefs - node["cpe_zoom"].compact + node['cpe_zoom'].compact end def convert_to_registry_key(value) if value == true - return { "data": 1, "type": :dword } + return { :data => 1, :type => :dword } elsif value == false - return { "data": 0, "type": :dword } + return { :data => 0, :type => :dword } else - return { "data": value.to_s, "type": :string } + return { :data => value.to_s, :type => :string } end end @@ -55,60 +55,65 @@ def configure_windows preference = WINDOWS_CHAT_SETTINGS_MAP[key] if preference.nil? # If the preference isn't in WINDOWS_CHAT_SETTINGS_MAP, set General path. - zoom_settings[WINDOWS_GENERAL_SETTINGS_MAP.fetch(key, key)] = { "path": 'HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Zoom\Zoom Meetings\General', "name": WINDOWS_GENERAL_SETTINGS_MAP.fetch(key, key) }.merge(convert_to_registry_key(value)) + zoom_settings[WINDOWS_GENERAL_SETTINGS_MAP.fetch(key, key)] = { + :path => 'HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Zoom\Zoom Meetings\General', + :name => WINDOWS_GENERAL_SETTINGS_MAP.fetch(key, key), + }.merge(convert_to_registry_key(value)) else - zoom_settings[preference] = { "path": 'HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Zoom\Zoom Meetings\chat', "name": key }.merge(convert_to_registry_key(value)) + zoom_settings[preference] = { + :path => 'HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Zoom\Zoom Meetings\chat', + :name => key, + }.merge(convert_to_registry_key(value)) end end unless zoom_settings.empty? - zoom_settings.each do | name, reg_key | + zoom_settings.each do |_name, reg_key| registry_key reg_key[:path] do - values [{ name: reg_key[:name], type: reg_key[:type], data: reg_key[:data] }] + values [{ :name => reg_key[:name], :type => reg_key[:type], :data => reg_key[:data] }] recursive true action :create end end end - end def configure_macos - prefix = node["cpe_profiles"]["prefix"] - organization = node["organization"] || "Uber" + prefix = node['cpe_profiles']['prefix'] + organization = node['organization'] || 'Uber' zoom_profile = { - "PayloadIdentifier" => "#{prefix}.zoom", - "PayloadRemovalDisallowed" => true, - "PayloadScope" => "System", - "PayloadType" => "Configuration", - "PayloadUUID" => "B1B0DEED-DC7C-4122-912F-A22F660DF53D", - "PayloadOrganization" => organization, - "PayloadVersion" => 1, - "PayloadDisplayName" => "Zoom", - "PayloadContent" => [], + 'PayloadIdentifier' => "#{prefix}.zoom", + 'PayloadRemovalDisallowed' => true, + 'PayloadScope' => 'System', + 'PayloadType' => 'Configuration', + 'PayloadUUID' => 'B1B0DEED-DC7C-4122-912F-A22F660DF53D', + 'PayloadOrganization' => organization, + 'PayloadVersion' => 1, + 'PayloadDisplayName' => 'Zoom', + 'PayloadContent' => [], } unless zoom_prefs.empty? - zoom_profile["PayloadContent"].push( - "PayloadType" => "us.zoom.config", - "PayloadVersion" => 1, - "PayloadIdentifier" => "#{prefix}.zoom", - "PayloadUUID" => "B976C3E1-B59D-4060-80DA-13A42270D1E7", - "PayloadEnabled" => true, - "PayloadDisplayName" => "Zoom", + zoom_profile['PayloadContent'].push( + 'PayloadType' => 'us.zoom.config', + 'PayloadVersion' => 1, + 'PayloadIdentifier' => "#{prefix}.zoom", + 'PayloadUUID' => 'B976C3E1-B59D-4060-80DA-13A42270D1E7', + 'PayloadEnabled' => true, + 'PayloadDisplayName' => 'Zoom', ) zoom_prefs.each_key do |key| next if zoom_prefs[key].nil? - zoom_profile["PayloadContent"][0][key] = zoom_prefs[key] + zoom_profile['PayloadContent'][0][key] = zoom_prefs[key] # Double tap the preferences since Zoom didn't use profiles at one point. Requires Chef 14 or newer macos_userdefaults "Configure us.zoom.config - #{key}" do - domain "/Library/Preferences/us.zoom.config" + domain '/Library/Preferences/us.zoom.config' key key value zoom_prefs[key] end end end - node.default["cpe_profiles"]["#{prefix}.zoom"] = zoom_profile + node.default['cpe_profiles']["#{prefix}.zoom"] = zoom_profile end end diff --git a/opensource_links.md b/opensource_links.md index 1d4824e..fe453c8 100644 --- a/opensource_links.md +++ b/opensource_links.md @@ -1,11 +1,12 @@ Chef API Cookbook Repos -- -* [Facebook](https://github.com/facebook/IT-CPE/tree/master/chef/cookbooks) +* [Facebook](https://github.com/facebook/IT-CPE/tree/main/itchef/cookbooks) +* [Gusto](https://github.com/Gusto/it-cpe-opensource/tree/main/chef) * [Pinterest](https://github.com/pinterest/it-cpe-cookbooks) -* [Uber](https://github.com/uber/cpe-chef-cookbooks) +* [Uber](https://github.com/uber/client-platform-engineering) -Opensource Projects +Open source Projects -- * [Crypt](https://github.com/grahamgilbert/crypt) * [Crypt-Server](https://github.com/grahamgilbert/Crypt-Server)