├── .devcontainer ├── Dockerfile ├── README.md └── devcontainer.json ├── .fixtures.yml ├── .gitattributes ├── .gitignore ├── .gitlab-ci.yml ├── .pdkignore ├── .puppet-lint.rc ├── .rspec ├── .rubocop.yml ├── .sync.yml ├── .travis.yml ├── .vscode └── extensions.json ├── .yardopts ├── CHANGELOG.md ├── CODEOWNERS ├── Gemfile ├── LICENSE ├── Puppetfile ├── README.md ├── Rakefile ├── appveyor.yml ├── bolt-project.yaml ├── examples └── params.json ├── lib └── puppet │ └── functions │ └── pecdm │ ├── is_windows.rb │ └── with_tempfile_containing.rb ├── metadata.json ├── pdk.yaml ├── pecdm.code-workspace ├── plans ├── destroy.pp ├── provision.pp ├── subplans │ ├── deploy.pp │ ├── destroy.pp │ └── provision.pp ├── upgrade.pp └── utils │ ├── deploy_agents.pp │ └── inventory_yaml.pp ├── scripts ├── README.md └── aws_bastion_mfa_export.sh ├── spec ├── default_facts.yml ├── plans │ ├── destroy_spec.rb │ ├── provision_spec.rb │ └── upgrade_spec.rb ├── spec_helper.rb └── spec_helper_local.rb └── templates ├── inventory_yaml.epp └── tfvars.epp /.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM puppet/pdk:latest 2 | 3 | # [Optional] Uncomment this section to install additional packages. 4 | # RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ 5 | # && apt-get -y install --no-install-recommends 6 | 7 | -------------------------------------------------------------------------------- /.devcontainer/README.md: -------------------------------------------------------------------------------- 1 | # devcontainer 2 | 3 | 4 | For format details, see https://aka.ms/devcontainer.json. 5 | 6 | For config options, see the README at: 7 | https://github.com/microsoft/vscode-dev-containers/tree/v0.140.1/containers/puppet 8 | 9 | ``` json 10 | { 11 | "name": "Puppet Development Kit (Community)", 12 | "dockerFile": "Dockerfile", 13 | 14 | // Set *default* container specific settings.json values on container create. 15 | "settings": { 16 | "terminal.integrated.profiles.linux": { 17 | "bash": { 18 | "path": "bash", 19 | } 20 | } 21 | }, 22 | 23 | // Add the IDs of extensions you want installed when the container is created. 24 | "extensions": [ 25 | "puppet.puppet-vscode", 26 | "rebornix.Ruby" 27 | ], 28 | 29 | // Use 'forwardPorts' to make a list of ports inside the container available locally. 30 | "forwardPorts": [], 31 | 32 | // Use 'postCreateCommand' to run commands after the container is created. 33 | "postCreateCommand": "pdk --version", 34 | } 35 | ``` 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Puppet Development Kit (Community)", 3 | "dockerFile": "Dockerfile", 4 | 5 | "settings": { 6 | "terminal.integrated.profiles.linux": { 7 | "bash": { 8 | "path": "bash" 9 | } 10 | } 11 | }, 12 | 13 | "extensions": [ 14 | "puppet.puppet-vscode", 15 | "rebornix.Ruby" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /.fixtures.yml: -------------------------------------------------------------------------------- 1 | # This file can be used to install module dependencies for unit testing 2 | # See https://github.com/puppetlabs/puppetlabs_spec_helper#using-fixtures for details 3 | --- 4 | fixtures: 5 | forge_modules: 6 | stdlib: "puppetlabs/stdlib" 7 | peadm: "puppetlabs/peadm" 8 | terraform: "puppetlabs/terraform" 9 | ruby_task_helper: "puppetlabs/ruby_task_helper" 10 | ruby_plugin_helper: "puppetlabs/ruby_plugin_helper" 11 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.rb eol=lf 2 | *.erb eol=lf 3 | *.pp eol=lf 4 | *.sh eol=lf 5 | *.epp eol=lf 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .git/ 2 | .*.sw[op] 3 | .metadata 4 | .yardoc 5 | .yardwarns 6 | *.iml 7 | /.bundle/ 8 | /.idea/ 9 | /.vagrant/ 10 | /coverage/ 11 | /bin/ 12 | /doc/ 13 | /Gemfile.local 14 | /Gemfile.lock 15 | /junit/ 16 | /log/ 17 | /pkg/ 18 | /spec/fixtures/manifests/ 19 | /spec/fixtures/modules/* 20 | /tmp/ 21 | /vendor/ 22 | /convert_report.txt 23 | /update_report.txt 24 | .DS_Store 25 | .project 26 | .envrc 27 | /spec/fixtures/litmus_inventory.yaml 28 | .modules 29 | .resource_types -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | --- 2 | stages: 3 | - syntax 4 | - unit 5 | 6 | default: 7 | cache: 8 | paths: 9 | - vendor/bundle 10 | 11 | before_script: &before_script 12 | - bundle -v 13 | - rm Gemfile.lock || true 14 | - "# Update system gems if requested. This is useful to temporarily workaround troubles in the test runner" 15 | - "# Set `rubygems_version` in the .sync.yml to set a value" 16 | - "# Ignore exit code of SIGPIPE'd yes to not fail with shell's pipefail set" 17 | - '[ -z "$RUBYGEMS_VERSION" ] || (yes || true) | gem update --system $RUBYGEMS_VERSION' 18 | - gem --version 19 | - bundle -v 20 | - bundle install --without system_tests --path vendor/bundle --jobs $(nproc) 21 | 22 | validate lint check rubocop-Ruby 2.5.7-Puppet ~> 6: 23 | stage: syntax 24 | image: ruby:2.5.7 25 | script: 26 | - bundle exec rake validate lint check rubocop 27 | variables: 28 | PUPPET_GEM_VERSION: '~> 6' 29 | 30 | parallel_spec-Ruby 2.5.7-Puppet ~> 6: 31 | stage: unit 32 | image: ruby:2.5.7 33 | script: 34 | - bundle exec rake parallel_spec 35 | variables: 36 | PUPPET_GEM_VERSION: '~> 6' 37 | 38 | validate lint check rubocop-Ruby 2.7.2-Puppet ~> 7: 39 | stage: syntax 40 | image: ruby:2.7.2 41 | script: 42 | - bundle exec rake validate lint check rubocop 43 | variables: 44 | PUPPET_GEM_VERSION: '~> 7' 45 | 46 | parallel_spec-Ruby 2.7.2-Puppet ~> 7: 47 | stage: unit 48 | image: ruby:2.7.2 49 | script: 50 | - bundle exec rake parallel_spec 51 | variables: 52 | PUPPET_GEM_VERSION: '~> 7' 53 | 54 | -------------------------------------------------------------------------------- /.pdkignore: -------------------------------------------------------------------------------- 1 | .git/ 2 | .*.sw[op] 3 | .metadata 4 | .yardoc 5 | .yardwarns 6 | *.iml 7 | /.bundle/ 8 | /.idea/ 9 | /.vagrant/ 10 | /coverage/ 11 | /bin/ 12 | /doc/ 13 | /Gemfile.local 14 | /Gemfile.lock 15 | /junit/ 16 | /log/ 17 | /pkg/ 18 | /spec/fixtures/manifests/ 19 | /spec/fixtures/modules/* 20 | /tmp/ 21 | /vendor/ 22 | /convert_report.txt 23 | /update_report.txt 24 | .DS_Store 25 | .project 26 | .envrc 27 | /spec/fixtures/litmus_inventory.yaml 28 | /.fixtures.yml 29 | /Gemfile 30 | /.gitattributes 31 | /.gitignore 32 | /.pdkignore 33 | /.puppet-lint.rc 34 | /Rakefile 35 | /rakelib/ 36 | /.rspec 37 | /..yml 38 | /.yardopts 39 | /spec/ 40 | /.vscode/ 41 | /.sync.yml 42 | /.devcontainer/ 43 | -------------------------------------------------------------------------------- /.puppet-lint.rc: -------------------------------------------------------------------------------- 1 | --relative 2 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | --format documentation 3 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | --- 2 | require: 3 | - rubocop-performance 4 | - rubocop-rspec 5 | AllCops: 6 | DisplayCopNames: true 7 | TargetRubyVersion: '2.6' 8 | Include: 9 | - "**/*.rb" 10 | Exclude: 11 | - bin/* 12 | - ".vendor/**/*" 13 | - "**/Gemfile" 14 | - "**/Rakefile" 15 | - pkg/**/* 16 | - spec/fixtures/**/* 17 | - vendor/**/* 18 | - "**/Puppetfile" 19 | - "**/Vagrantfile" 20 | - "**/Guardfile" 21 | Layout/LineLength: 22 | Description: People have wide screens, use them. 23 | Max: 200 24 | RSpec/BeforeAfterAll: 25 | Description: Beware of using after(:all) as it may cause state to leak between tests. 26 | A necessary evil in acceptance testing. 27 | Exclude: 28 | - spec/acceptance/**/*.rb 29 | RSpec/HookArgument: 30 | Description: Prefer explicit :each argument, matching existing module's style 31 | EnforcedStyle: each 32 | RSpec/DescribeSymbol: 33 | Exclude: 34 | - spec/unit/facter/**/*.rb 35 | Style/BlockDelimiters: 36 | Description: Prefer braces for chaining. Mostly an aesthetical choice. Better to 37 | be consistent then. 38 | EnforcedStyle: braces_for_chaining 39 | Style/ClassAndModuleChildren: 40 | Description: Compact style reduces the required amount of indentation. 41 | EnforcedStyle: compact 42 | Style/EmptyElse: 43 | Description: Enforce against empty else clauses, but allow `nil` for clarity. 44 | EnforcedStyle: empty 45 | Style/FormatString: 46 | Description: Following the main puppet project's style, prefer the % format format. 47 | EnforcedStyle: percent 48 | Style/FormatStringToken: 49 | Description: Following the main puppet project's style, prefer the simpler template 50 | tokens over annotated ones. 51 | EnforcedStyle: template 52 | Style/Lambda: 53 | Description: Prefer the keyword for easier discoverability. 54 | EnforcedStyle: literal 55 | Style/RegexpLiteral: 56 | Description: Community preference. See https://github.com/voxpupuli/modulesync_config/issues/168 57 | EnforcedStyle: percent_r 58 | Style/TernaryParentheses: 59 | Description: Checks for use of parentheses around ternary conditions. Enforce parentheses 60 | on complex expressions for better readability, but seriously consider breaking 61 | it up. 62 | EnforcedStyle: require_parentheses_when_complex 63 | Style/TrailingCommaInArguments: 64 | Description: Prefer always trailing comma on multiline argument lists. This makes 65 | diffs, and re-ordering nicer. 66 | EnforcedStyleForMultiline: comma 67 | Style/TrailingCommaInArrayLiteral: 68 | Description: Prefer always trailing comma on multiline literals. This makes diffs, 69 | and re-ordering nicer. 70 | EnforcedStyleForMultiline: comma 71 | Style/SymbolArray: 72 | Description: Using percent style obscures symbolic intent of array's contents. 73 | EnforcedStyle: brackets 74 | RSpec/MessageSpies: 75 | EnforcedStyle: receive 76 | Style/Documentation: 77 | Exclude: 78 | - lib/puppet/parser/functions/**/* 79 | - spec/**/* 80 | Style/WordArray: 81 | EnforcedStyle: brackets 82 | Performance/AncestorsInclude: 83 | Enabled: true 84 | Performance/BigDecimalWithNumericArgument: 85 | Enabled: true 86 | Performance/BlockGivenWithExplicitBlock: 87 | Enabled: true 88 | Performance/CaseWhenSplat: 89 | Enabled: true 90 | Performance/ConstantRegexp: 91 | Enabled: true 92 | Performance/MethodObjectAsBlock: 93 | Enabled: true 94 | Performance/RedundantSortBlock: 95 | Enabled: true 96 | Performance/RedundantStringChars: 97 | Enabled: true 98 | Performance/ReverseFirst: 99 | Enabled: true 100 | Performance/SortReverse: 101 | Enabled: true 102 | Performance/Squeeze: 103 | Enabled: true 104 | Performance/StringInclude: 105 | Enabled: true 106 | Performance/Sum: 107 | Enabled: true 108 | Style/CollectionMethods: 109 | Enabled: true 110 | Style/MethodCalledOnDoEndBlock: 111 | Enabled: true 112 | Style/StringMethods: 113 | Enabled: true 114 | Bundler/GemFilename: 115 | Enabled: false 116 | Bundler/InsecureProtocolSource: 117 | Enabled: false 118 | Capybara/CurrentPathExpectation: 119 | Enabled: false 120 | Capybara/VisibilityMatcher: 121 | Enabled: false 122 | Gemspec/DuplicatedAssignment: 123 | Enabled: false 124 | Gemspec/OrderedDependencies: 125 | Enabled: false 126 | Gemspec/RequiredRubyVersion: 127 | Enabled: false 128 | Gemspec/RubyVersionGlobalsUsage: 129 | Enabled: false 130 | Layout/ArgumentAlignment: 131 | Enabled: false 132 | Layout/BeginEndAlignment: 133 | Enabled: false 134 | Layout/ClosingHeredocIndentation: 135 | Enabled: false 136 | Layout/EmptyComment: 137 | Enabled: false 138 | Layout/EmptyLineAfterGuardClause: 139 | Enabled: false 140 | Layout/EmptyLinesAroundArguments: 141 | Enabled: false 142 | Layout/EmptyLinesAroundAttributeAccessor: 143 | Enabled: false 144 | Layout/EndOfLine: 145 | Enabled: false 146 | Layout/FirstArgumentIndentation: 147 | Enabled: false 148 | Layout/HashAlignment: 149 | Enabled: false 150 | Layout/HeredocIndentation: 151 | Enabled: false 152 | Layout/LeadingEmptyLines: 153 | Enabled: false 154 | Layout/SpaceAroundMethodCallOperator: 155 | Enabled: false 156 | Layout/SpaceInsideArrayLiteralBrackets: 157 | Enabled: false 158 | Layout/SpaceInsideReferenceBrackets: 159 | Enabled: false 160 | Lint/BigDecimalNew: 161 | Enabled: false 162 | Lint/BooleanSymbol: 163 | Enabled: false 164 | Lint/ConstantDefinitionInBlock: 165 | Enabled: false 166 | Lint/DeprecatedOpenSSLConstant: 167 | Enabled: false 168 | Lint/DisjunctiveAssignmentInConstructor: 169 | Enabled: false 170 | Lint/DuplicateElsifCondition: 171 | Enabled: false 172 | Lint/DuplicateRequire: 173 | Enabled: false 174 | Lint/DuplicateRescueException: 175 | Enabled: false 176 | Lint/EmptyConditionalBody: 177 | Enabled: false 178 | Lint/EmptyFile: 179 | Enabled: false 180 | Lint/ErbNewArguments: 181 | Enabled: false 182 | Lint/FloatComparison: 183 | Enabled: false 184 | Lint/HashCompareByIdentity: 185 | Enabled: false 186 | Lint/IdentityComparison: 187 | Enabled: false 188 | Lint/InterpolationCheck: 189 | Enabled: false 190 | Lint/MissingCopEnableDirective: 191 | Enabled: false 192 | Lint/MixedRegexpCaptureTypes: 193 | Enabled: false 194 | Lint/NestedPercentLiteral: 195 | Enabled: false 196 | Lint/NonDeterministicRequireOrder: 197 | Enabled: false 198 | Lint/OrderedMagicComments: 199 | Enabled: false 200 | Lint/OutOfRangeRegexpRef: 201 | Enabled: false 202 | Lint/RaiseException: 203 | Enabled: false 204 | Lint/RedundantCopEnableDirective: 205 | Enabled: false 206 | Lint/RedundantRequireStatement: 207 | Enabled: false 208 | Lint/RedundantSafeNavigation: 209 | Enabled: false 210 | Lint/RedundantWithIndex: 211 | Enabled: false 212 | Lint/RedundantWithObject: 213 | Enabled: false 214 | Lint/RegexpAsCondition: 215 | Enabled: false 216 | Lint/ReturnInVoidContext: 217 | Enabled: false 218 | Lint/SafeNavigationConsistency: 219 | Enabled: false 220 | Lint/SafeNavigationWithEmpty: 221 | Enabled: false 222 | Lint/SelfAssignment: 223 | Enabled: false 224 | Lint/SendWithMixinArgument: 225 | Enabled: false 226 | Lint/ShadowedArgument: 227 | Enabled: false 228 | Lint/StructNewOverride: 229 | Enabled: false 230 | Lint/ToJSON: 231 | Enabled: false 232 | Lint/TopLevelReturnWithArgument: 233 | Enabled: false 234 | Lint/TrailingCommaInAttributeDeclaration: 235 | Enabled: false 236 | Lint/UnreachableLoop: 237 | Enabled: false 238 | Lint/UriEscapeUnescape: 239 | Enabled: false 240 | Lint/UriRegexp: 241 | Enabled: false 242 | Lint/UselessMethodDefinition: 243 | Enabled: false 244 | Lint/UselessTimes: 245 | Enabled: false 246 | Metrics/AbcSize: 247 | Enabled: false 248 | Metrics/BlockLength: 249 | Enabled: false 250 | Metrics/BlockNesting: 251 | Enabled: false 252 | Metrics/ClassLength: 253 | Enabled: false 254 | Metrics/CyclomaticComplexity: 255 | Enabled: false 256 | Metrics/MethodLength: 257 | Enabled: false 258 | Metrics/ModuleLength: 259 | Enabled: false 260 | Metrics/ParameterLists: 261 | Enabled: false 262 | Metrics/PerceivedComplexity: 263 | Enabled: false 264 | Migration/DepartmentName: 265 | Enabled: false 266 | Naming/AccessorMethodName: 267 | Enabled: false 268 | Naming/BlockParameterName: 269 | Enabled: false 270 | Naming/HeredocDelimiterCase: 271 | Enabled: false 272 | Naming/HeredocDelimiterNaming: 273 | Enabled: false 274 | Naming/MemoizedInstanceVariableName: 275 | Enabled: false 276 | Naming/MethodParameterName: 277 | Enabled: false 278 | Naming/RescuedExceptionsVariableName: 279 | Enabled: false 280 | Naming/VariableNumber: 281 | Enabled: false 282 | Performance/BindCall: 283 | Enabled: false 284 | Performance/DeletePrefix: 285 | Enabled: false 286 | Performance/DeleteSuffix: 287 | Enabled: false 288 | Performance/InefficientHashSearch: 289 | Enabled: false 290 | Performance/UnfreezeString: 291 | Enabled: false 292 | Performance/UriDefaultParser: 293 | Enabled: false 294 | RSpec/Be: 295 | Enabled: false 296 | RSpec/Capybara/FeatureMethods: 297 | Enabled: false 298 | RSpec/ContainExactly: 299 | Enabled: false 300 | RSpec/ContextMethod: 301 | Enabled: false 302 | RSpec/ContextWording: 303 | Enabled: false 304 | RSpec/DescribeClass: 305 | Enabled: false 306 | RSpec/EmptyHook: 307 | Enabled: false 308 | RSpec/EmptyLineAfterExample: 309 | Enabled: false 310 | RSpec/EmptyLineAfterExampleGroup: 311 | Enabled: false 312 | RSpec/EmptyLineAfterHook: 313 | Enabled: false 314 | RSpec/ExampleLength: 315 | Enabled: false 316 | RSpec/ExampleWithoutDescription: 317 | Enabled: false 318 | RSpec/ExpectChange: 319 | Enabled: false 320 | RSpec/ExpectInHook: 321 | Enabled: false 322 | RSpec/FactoryBot/AttributeDefinedStatically: 323 | Enabled: false 324 | RSpec/FactoryBot/CreateList: 325 | Enabled: false 326 | RSpec/FactoryBot/FactoryClassName: 327 | Enabled: false 328 | RSpec/HooksBeforeExamples: 329 | Enabled: false 330 | RSpec/ImplicitBlockExpectation: 331 | Enabled: false 332 | RSpec/ImplicitSubject: 333 | Enabled: false 334 | RSpec/LeakyConstantDeclaration: 335 | Enabled: false 336 | RSpec/LetBeforeExamples: 337 | Enabled: false 338 | RSpec/MatchArray: 339 | Enabled: false 340 | RSpec/MissingExampleGroupArgument: 341 | Enabled: false 342 | RSpec/MultipleExpectations: 343 | Enabled: false 344 | RSpec/MultipleMemoizedHelpers: 345 | Enabled: false 346 | RSpec/MultipleSubjects: 347 | Enabled: false 348 | RSpec/NestedGroups: 349 | Enabled: false 350 | RSpec/PredicateMatcher: 351 | Enabled: false 352 | RSpec/ReceiveCounts: 353 | Enabled: false 354 | RSpec/ReceiveNever: 355 | Enabled: false 356 | RSpec/RepeatedExampleGroupBody: 357 | Enabled: false 358 | RSpec/RepeatedExampleGroupDescription: 359 | Enabled: false 360 | RSpec/RepeatedIncludeExample: 361 | Enabled: false 362 | RSpec/ReturnFromStub: 363 | Enabled: false 364 | RSpec/SharedExamples: 365 | Enabled: false 366 | RSpec/StubbedMock: 367 | Enabled: false 368 | RSpec/UnspecifiedException: 369 | Enabled: false 370 | RSpec/VariableDefinition: 371 | Enabled: false 372 | RSpec/VoidExpect: 373 | Enabled: false 374 | RSpec/Yield: 375 | Enabled: false 376 | Security/Open: 377 | Enabled: false 378 | Style/AccessModifierDeclarations: 379 | Enabled: false 380 | Style/AccessorGrouping: 381 | Enabled: false 382 | Style/BisectedAttrAccessor: 383 | Enabled: false 384 | Style/CaseLikeIf: 385 | Enabled: false 386 | Style/ClassEqualityComparison: 387 | Enabled: false 388 | Style/ColonMethodDefinition: 389 | Enabled: false 390 | Style/CombinableLoops: 391 | Enabled: false 392 | Style/CommentedKeyword: 393 | Enabled: false 394 | Style/Dir: 395 | Enabled: false 396 | Style/DoubleCopDisableDirective: 397 | Enabled: false 398 | Style/EmptyBlockParameter: 399 | Enabled: false 400 | Style/EmptyLambdaParameter: 401 | Enabled: false 402 | Style/Encoding: 403 | Enabled: false 404 | Style/EvalWithLocation: 405 | Enabled: false 406 | Style/ExpandPathArguments: 407 | Enabled: false 408 | Style/ExplicitBlockArgument: 409 | Enabled: false 410 | Style/ExponentialNotation: 411 | Enabled: false 412 | Style/FloatDivision: 413 | Enabled: false 414 | Style/FrozenStringLiteralComment: 415 | Enabled: false 416 | Style/GlobalStdStream: 417 | Enabled: false 418 | Style/HashAsLastArrayItem: 419 | Enabled: false 420 | Style/HashLikeCase: 421 | Enabled: false 422 | Style/HashTransformKeys: 423 | Enabled: false 424 | Style/HashTransformValues: 425 | Enabled: false 426 | Style/IfUnlessModifier: 427 | Enabled: false 428 | Style/KeywordParametersOrder: 429 | Enabled: false 430 | Style/MinMax: 431 | Enabled: false 432 | Style/MixinUsage: 433 | Enabled: false 434 | Style/MultilineWhenThen: 435 | Enabled: false 436 | Style/NegatedUnless: 437 | Enabled: false 438 | Style/NumericPredicate: 439 | Enabled: false 440 | Style/OptionalBooleanParameter: 441 | Enabled: false 442 | Style/OrAssignment: 443 | Enabled: false 444 | Style/RandomWithOffset: 445 | Enabled: false 446 | Style/RedundantAssignment: 447 | Enabled: false 448 | Style/RedundantCondition: 449 | Enabled: false 450 | Style/RedundantConditional: 451 | Enabled: false 452 | Style/RedundantFetchBlock: 453 | Enabled: false 454 | Style/RedundantFileExtensionInRequire: 455 | Enabled: false 456 | Style/RedundantRegexpCharacterClass: 457 | Enabled: false 458 | Style/RedundantRegexpEscape: 459 | Enabled: false 460 | Style/RedundantSelfAssignment: 461 | Enabled: false 462 | Style/RedundantSort: 463 | Enabled: false 464 | Style/RescueStandardError: 465 | Enabled: false 466 | Style/SingleArgumentDig: 467 | Enabled: false 468 | Style/SlicingWithRange: 469 | Enabled: false 470 | Style/SoleNestedConditional: 471 | Enabled: false 472 | Style/StderrPuts: 473 | Enabled: false 474 | Style/StringConcatenation: 475 | Enabled: false 476 | Style/Strip: 477 | Enabled: false 478 | Style/SymbolProc: 479 | Enabled: false 480 | Style/TrailingBodyOnClass: 481 | Enabled: false 482 | Style/TrailingBodyOnMethodDefinition: 483 | Enabled: false 484 | Style/TrailingBodyOnModule: 485 | Enabled: false 486 | Style/TrailingCommaInHashLiteral: 487 | Enabled: false 488 | Style/TrailingMethodEndStatement: 489 | Enabled: false 490 | Style/UnpackFirst: 491 | Enabled: false 492 | Capybara/MatchStyle: 493 | Enabled: false 494 | Capybara/NegationMatcher: 495 | Enabled: false 496 | Capybara/SpecificActions: 497 | Enabled: false 498 | Capybara/SpecificFinders: 499 | Enabled: false 500 | Capybara/SpecificMatcher: 501 | Enabled: false 502 | Gemspec/DeprecatedAttributeAssignment: 503 | Enabled: false 504 | Gemspec/DevelopmentDependencies: 505 | Enabled: false 506 | Gemspec/RequireMFA: 507 | Enabled: false 508 | Layout/LineContinuationLeadingSpace: 509 | Enabled: false 510 | Layout/LineContinuationSpacing: 511 | Enabled: false 512 | Layout/LineEndStringConcatenationIndentation: 513 | Enabled: false 514 | Layout/SpaceBeforeBrackets: 515 | Enabled: false 516 | Lint/AmbiguousAssignment: 517 | Enabled: false 518 | Lint/AmbiguousOperatorPrecedence: 519 | Enabled: false 520 | Lint/AmbiguousRange: 521 | Enabled: false 522 | Lint/ConstantOverwrittenInRescue: 523 | Enabled: false 524 | Lint/DeprecatedConstants: 525 | Enabled: false 526 | Lint/DuplicateBranch: 527 | Enabled: false 528 | Lint/DuplicateMagicComment: 529 | Enabled: false 530 | Lint/DuplicateRegexpCharacterClassElement: 531 | Enabled: false 532 | Lint/EmptyBlock: 533 | Enabled: false 534 | Lint/EmptyClass: 535 | Enabled: false 536 | Lint/EmptyInPattern: 537 | Enabled: false 538 | Lint/IncompatibleIoSelectWithFiberScheduler: 539 | Enabled: false 540 | Lint/LambdaWithoutLiteralBlock: 541 | Enabled: false 542 | Lint/NoReturnInBeginEndBlocks: 543 | Enabled: false 544 | Lint/NonAtomicFileOperation: 545 | Enabled: false 546 | Lint/NumberedParameterAssignment: 547 | Enabled: false 548 | Lint/OrAssignmentToConstant: 549 | Enabled: false 550 | Lint/RedundantDirGlobSort: 551 | Enabled: false 552 | Lint/RefinementImportMethods: 553 | Enabled: false 554 | Lint/RequireRangeParentheses: 555 | Enabled: false 556 | Lint/RequireRelativeSelfPath: 557 | Enabled: false 558 | Lint/SymbolConversion: 559 | Enabled: false 560 | Lint/ToEnumArguments: 561 | Enabled: false 562 | Lint/TripleQuotes: 563 | Enabled: false 564 | Lint/UnexpectedBlockArity: 565 | Enabled: false 566 | Lint/UnmodifiedReduceAccumulator: 567 | Enabled: false 568 | Lint/UselessRescue: 569 | Enabled: false 570 | Lint/UselessRuby2Keywords: 571 | Enabled: false 572 | Metrics/CollectionLiteralLength: 573 | Enabled: false 574 | Naming/BlockForwarding: 575 | Enabled: false 576 | Performance/CollectionLiteralInLoop: 577 | Enabled: false 578 | Performance/ConcurrentMonotonicTime: 579 | Enabled: false 580 | Performance/MapCompact: 581 | Enabled: false 582 | Performance/RedundantEqualityComparisonBlock: 583 | Enabled: false 584 | Performance/RedundantSplitRegexpArgument: 585 | Enabled: false 586 | Performance/StringIdentifierArgument: 587 | Enabled: false 588 | RSpec/BeEq: 589 | Enabled: false 590 | RSpec/BeNil: 591 | Enabled: false 592 | RSpec/ChangeByZero: 593 | Enabled: false 594 | RSpec/ClassCheck: 595 | Enabled: false 596 | RSpec/DuplicatedMetadata: 597 | Enabled: false 598 | RSpec/ExcessiveDocstringSpacing: 599 | Enabled: false 600 | RSpec/FactoryBot/ConsistentParenthesesStyle: 601 | Enabled: false 602 | RSpec/FactoryBot/FactoryNameStyle: 603 | Enabled: false 604 | RSpec/FactoryBot/SyntaxMethods: 605 | Enabled: false 606 | RSpec/IdenticalEqualityAssertion: 607 | Enabled: false 608 | RSpec/NoExpectationExample: 609 | Enabled: false 610 | RSpec/PendingWithoutReason: 611 | Enabled: false 612 | RSpec/Rails/AvoidSetupHook: 613 | Enabled: false 614 | RSpec/Rails/HaveHttpStatus: 615 | Enabled: false 616 | RSpec/Rails/InferredSpecType: 617 | Enabled: false 618 | RSpec/Rails/MinitestAssertions: 619 | Enabled: false 620 | RSpec/Rails/TravelAround: 621 | Enabled: false 622 | RSpec/RedundantAround: 623 | Enabled: false 624 | RSpec/SkipBlockInsideExample: 625 | Enabled: false 626 | RSpec/SortMetadata: 627 | Enabled: false 628 | RSpec/SubjectDeclaration: 629 | Enabled: false 630 | RSpec/VerifiedDoubleReference: 631 | Enabled: false 632 | Security/CompoundHash: 633 | Enabled: false 634 | Security/IoMethods: 635 | Enabled: false 636 | Style/ArgumentsForwarding: 637 | Enabled: false 638 | Style/ArrayIntersect: 639 | Enabled: false 640 | Style/CollectionCompact: 641 | Enabled: false 642 | Style/ComparableClamp: 643 | Enabled: false 644 | Style/ConcatArrayLiterals: 645 | Enabled: false 646 | Style/DirEmpty: 647 | Enabled: false 648 | Style/DocumentDynamicEvalDefinition: 649 | Enabled: false 650 | Style/EmptyHeredoc: 651 | Enabled: false 652 | Style/EndlessMethod: 653 | Enabled: false 654 | Style/EnvHome: 655 | Enabled: false 656 | Style/FetchEnvVar: 657 | Enabled: false 658 | Style/FileEmpty: 659 | Enabled: false 660 | Style/FileRead: 661 | Enabled: false 662 | Style/FileWrite: 663 | Enabled: false 664 | Style/HashConversion: 665 | Enabled: false 666 | Style/HashExcept: 667 | Enabled: false 668 | Style/IfWithBooleanLiteralBranches: 669 | Enabled: false 670 | Style/InPatternThen: 671 | Enabled: false 672 | Style/MagicCommentFormat: 673 | Enabled: false 674 | Style/MapCompactWithConditionalBlock: 675 | Enabled: false 676 | Style/MapToHash: 677 | Enabled: false 678 | Style/MapToSet: 679 | Enabled: false 680 | Style/MinMaxComparison: 681 | Enabled: false 682 | Style/MultilineInPatternThen: 683 | Enabled: false 684 | Style/NegatedIfElseCondition: 685 | Enabled: false 686 | Style/NestedFileDirname: 687 | Enabled: false 688 | Style/NilLambda: 689 | Enabled: false 690 | Style/NumberedParameters: 691 | Enabled: false 692 | Style/NumberedParametersLimit: 693 | Enabled: false 694 | Style/ObjectThen: 695 | Enabled: false 696 | Style/OpenStructUse: 697 | Enabled: false 698 | Style/OperatorMethodCall: 699 | Enabled: false 700 | Style/QuotedSymbols: 701 | Enabled: false 702 | Style/RedundantArgument: 703 | Enabled: false 704 | Style/RedundantConstantBase: 705 | Enabled: false 706 | Style/RedundantDoubleSplatHashBraces: 707 | Enabled: false 708 | Style/RedundantEach: 709 | Enabled: false 710 | Style/RedundantHeredocDelimiterQuotes: 711 | Enabled: false 712 | Style/RedundantInitialize: 713 | Enabled: false 714 | Style/RedundantSelfAssignmentBranch: 715 | Enabled: false 716 | Style/RedundantStringEscape: 717 | Enabled: false 718 | Style/SelectByRegexp: 719 | Enabled: false 720 | Style/StringChars: 721 | Enabled: false 722 | Style/SwapValues: 723 | Enabled: false 724 | -------------------------------------------------------------------------------- /.sync.yml: -------------------------------------------------------------------------------- 1 | --- 2 | .gitignore: 3 | required: 4 | - '---/inventory.yaml' 5 | Gemfile: 6 | required: 7 | ':development': 8 | - gem: 'bolt' 9 | version: '>= 3.10.0' 10 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | --- 2 | os: linux 3 | dist: xenial 4 | language: ruby 5 | cache: bundler 6 | before_install: 7 | - bundle -v 8 | - rm -f Gemfile.lock 9 | - "# Update system gems if requested. This is useful to temporarily workaround troubles in the test runner" 10 | - "# See https://github.com/puppetlabs/pdk-templates/commit/705154d5c437796b821691b707156e1b056d244f for an example of how this was used" 11 | - "# Ignore exit code of SIGPIPE'd yes to not fail with shell's pipefail set" 12 | - '[ -z "$RUBYGEMS_VERSION" ] || (yes || true) | gem update --system $RUBYGEMS_VERSION' 13 | - gem --version 14 | - bundle -v 15 | script: 16 | - 'bundle exec rake $CHECK' 17 | bundler_args: --without system_tests 18 | rvm: 19 | - 2.5.7 20 | stages: 21 | - static 22 | - spec 23 | - acceptance 24 | - 25 | if: tag =~ ^v\d 26 | name: deploy 27 | jobs: 28 | fast_finish: true 29 | include: 30 | - 31 | env: CHECK="validate lint check rubocop" 32 | stage: static 33 | - 34 | env: PUPPET_GEM_VERSION="~> 6.0" CHECK=parallel_spec 35 | rvm: 2.5.7 36 | stage: spec 37 | - 38 | env: DEPLOY_TO_FORGE=yes 39 | stage: deploy 40 | branches: 41 | only: 42 | - main 43 | - /^v\d/ 44 | notifications: 45 | email: false 46 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "puppet.puppet-vscode", 4 | "rebornix.Ruby" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /.yardopts: -------------------------------------------------------------------------------- 1 | --markup markdown 2 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | ## Release 0.1.0 6 | 7 | **Features** 8 | 9 | **Bugfixes** 10 | 11 | **Known Issues** 12 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | # This is a comment. 2 | # Each line is a file pattern followed by one or more owners. 3 | 4 | # This owner will be the default owners for everything in 5 | # the repo. Unless a later match takes precedence, 6 | # this owner will be requested for review when someone opens 7 | # a pull request. 8 | * @puppetlabs/solutions-architecture 9 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source ENV['GEM_SOURCE'] || 'https://rubygems.org' 2 | 3 | def location_for(place_or_version, fake_version = nil) 4 | git_url_regex = %r{\A(?(https?|git)[:@][^#]*)(#(?.*))?} 5 | file_url_regex = %r{\Afile:\/\/(?.*)} 6 | 7 | if place_or_version && (git_url = place_or_version.match(git_url_regex)) 8 | [fake_version, { git: git_url[:url], branch: git_url[:branch], require: false }].compact 9 | elsif place_or_version && (file_url = place_or_version.match(file_url_regex)) 10 | ['>= 0', { path: File.expand_path(file_url[:path]), require: false }] 11 | else 12 | [place_or_version, { require: false }] 13 | end 14 | end 15 | 16 | group :development do 17 | gem "json", '= 2.1.0', require: false if Gem::Requirement.create(['>= 2.5.0', '< 2.7.0']).satisfied_by?(Gem::Version.new(RUBY_VERSION.dup)) 18 | gem "json", '= 2.3.0', require: false if Gem::Requirement.create(['>= 2.7.0', '< 3.0.0']).satisfied_by?(Gem::Version.new(RUBY_VERSION.dup)) 19 | gem "json", '= 2.5.1', require: false if Gem::Requirement.create(['>= 3.0.0', '< 3.0.5']).satisfied_by?(Gem::Version.new(RUBY_VERSION.dup)) 20 | gem "json", '= 2.6.1', require: false if Gem::Requirement.create(['>= 3.1.0', '< 3.1.3']).satisfied_by?(Gem::Version.new(RUBY_VERSION.dup)) 21 | gem "json", '= 2.6.3', require: false if Gem::Requirement.create(['>= 3.2.0', '< 4.0.0']).satisfied_by?(Gem::Version.new(RUBY_VERSION.dup)) 22 | gem "racc", '~> 1.4.0', require: false if Gem::Requirement.create(['>= 2.7.0', '< 3.0.0']).satisfied_by?(Gem::Version.new(RUBY_VERSION.dup)) 23 | gem "voxpupuli-puppet-lint-plugins", '~> 5.0', require: false 24 | gem "facterdb", '~> 1.18', require: false 25 | gem "metadata-json-lint", '~> 3.0', require: false 26 | gem "puppetlabs_spec_helper", '~> 6.0', require: false 27 | gem "rspec-puppet-facts", '~> 2.0', require: false 28 | gem "codecov", '~> 0.2', require: false 29 | gem "dependency_checker", '~> 1.0.0', require: false 30 | gem "parallel_tests", '= 3.12.1', require: false 31 | gem "pry", '~> 0.10', require: false 32 | gem "simplecov-console", '~> 0.5', require: false 33 | gem "puppet-debugger", '~> 1.0', require: false 34 | gem "rubocop", '= 1.48.1', require: false 35 | gem "rubocop-performance", '= 1.16.0', require: false 36 | gem "rubocop-rspec", '= 2.19.0', require: false 37 | gem "rb-readline", '= 0.5.5', require: false, platforms: [:mswin, :mingw, :x64_mingw] 38 | gem "bolt", '>= 3.10.0', require: false 39 | end 40 | group :system_tests do 41 | gem "puppet_litmus", '~> 1.0', require: false, platforms: [:ruby, :x64_mingw] 42 | gem "serverspec", '~> 2.41', require: false 43 | end 44 | 45 | puppet_version = ENV['PUPPET_GEM_VERSION'] 46 | facter_version = ENV['FACTER_GEM_VERSION'] 47 | hiera_version = ENV['HIERA_GEM_VERSION'] 48 | 49 | gems = {} 50 | 51 | gems['puppet'] = location_for(puppet_version) 52 | 53 | # If facter or hiera versions have been specified via the environment 54 | # variables 55 | 56 | gems['facter'] = location_for(facter_version) if facter_version 57 | gems['hiera'] = location_for(hiera_version) if hiera_version 58 | 59 | gems.each do |gem_name, gem_params| 60 | gem gem_name, *gem_params 61 | end 62 | 63 | # Evaluate Gemfile.local and ~/.gemfile if they exist 64 | extra_gemfiles = [ 65 | "#{__FILE__}.local", 66 | File.join(Dir.home, '.gemfile'), 67 | ] 68 | 69 | extra_gemfiles.each do |gemfile| 70 | if File.file?(gemfile) && File.readable?(gemfile) 71 | eval(File.read(gemfile), binding) 72 | end 73 | end 74 | # vim: syntax=ruby 75 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /Puppetfile: -------------------------------------------------------------------------------- 1 | forge 'https://forge.puppet.com' 2 | 3 | mod 'puppetlabs-stdlib', '9.4.0' 4 | mod 'puppetlabs-apply_helpers', '0.3.0' 5 | mod 'puppetlabs-bolt_shim', '0.4.0' 6 | mod 'puppetlabs-inifile', '6.1.0' 7 | mod 'WhatsARanjit-node_manager', '0.8.0' 8 | mod 'puppetlabs-ruby_task_helper', '0.6.1' 9 | mod 'puppetlabs-ruby_plugin_helper', '0.2.0' 10 | mod 'puppetlabs-peadm', '3.19.0' 11 | # mod 'puppetlabs-terraform', '0.7.0' 12 | mod 'puppetlabs-terraform', 13 | git: 'https://github.com/puppetlabs/puppetlabs-terraform.git', 14 | ref: 'ce7b0070e7d7ec3b683df7dffb7fce745eca06e6' 15 | 16 | # External non-Puppet content 17 | # 18 | # Not a perfect solution given some assumptions made by r10k about repository 19 | # naming, specifically that there can only be one "-" or "/" in the name and 20 | # the component preceding those characters is dropped. These assumptions make 21 | # using content from other tool that follow a different naming pattern 22 | # sub-optimal but ultimately the on disk name and the name of the source 23 | # repository are not required to match and naming is irrelevant to Bolt when the 24 | # content is outside the modules and site-modules directories. 25 | # 26 | mod 'terraform-google_pe_arch', 27 | git: 'https://github.com/puppetlabs/terraform-google-pe_arch.git', 28 | ref: '5772aebc633f72acb031d6c4b5f786e99a451a65', 29 | install_path: '.terraform' 30 | 31 | mod 'terraform-aws_pe_arch', 32 | git: 'https://github.com/puppetlabs/terraform-aws-pe_arch.git', 33 | ref: 'e5a731c727e73e50d95020a0f2913f86f4ec9c1c', 34 | install_path: '.terraform' 35 | 36 | mod 'terraform-azure_pe_arch', 37 | git: 'https://github.com/puppetlabs/terraform-azure-pe_arch.git', 38 | ref: '0c362beddede71a83e10f76e873d85da01d9acea', 39 | install_path: '.terraform' 40 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Puppet Enterprise (pe) Cloud Deployment Module (cdm) 2 | 3 | Puppet Enterprise Cloud Deployment Module is a fusion of [puppetlabs/peadm](https://github.com/puppetlabs/puppetlabs-peadm) and Terraform. 4 | 5 | This project was referred to as **autope** prior to October 15, 2021. 6 | 7 | **Table of Contents** 8 | 9 | 1. [Description](#description) 10 | 2. [Setup - The basics of getting started with pecdm](#setup) 11 | * [What pecdm affects](#what-pecdm-affects) 12 | * [Setup requirements](#setup-requirements) 13 | * [Beginning with pecdm](#beginning-with-pecdm) 14 | 3. [Usage - Configuration options and additional functionality](#usage) 15 | * [Adding user specific deployment requirements](#adding-user-specific-deployment-requirements) 16 | 4. [Limitations - OS compatibility, etc.](#limitations) 17 | 5. [Development - Guide for contributing to the module](#development) 18 | 19 | ## Description 20 | 21 | The pecdm Bolt project demonstrates how you can link together automation tools to take advantage of their strengths, e.g. Terraform for infrastructure provisioning and Puppet for infrastructure configuration. We take [puppetlabs/peadm](https://github.com/puppetlabs/puppetlabs-peadm) and a Terraform module ([GCP](https://github.com/puppetlabs/terraform-google-pe_arch), [AWS](https://github.com/puppetlabs/terraform-aws-pe_arch), [Azure](https://github.com/puppetlabs/terraform-azure-pe_arch)) to facilitate rapid and repeatable deployments of Puppet Enterprise built upon the Standard, Large or Extra Large architecture w/ optional fail over replica. 22 | 23 | ## Expectations and support 24 | 25 | The pecdm Bolt project is developed by Puppet's Solutions Architecture team, intended as an internal tool to make the deployment of disposable stacks of Puppet Enterprise easy to deploy for the reproduction of customer issues, internal development, and demonstrations. Independent use is not recommended for production environments but with a comprehensive understanding of how Puppet Enterprise, Puppet Bolt, and Terraform work, plus high levels of comfort with the modification and maintenance of Terraform code and the infrastructure requirements of a full Puppet Enterprise deployment it could be referenced as an example for building your own automated solution. 26 | 27 | The pecdm Bolt project is an internal tool, and is **NOT** supported through Puppet Enterprise's standard or premium support.puppet.com service. 28 | 29 | If you are a Puppet Enterprise customer and come upon this project and wish to provide feedback, submit a feature request, or bugfix then please do so through the [Issues](https://github.com/puppetlabs/puppetlabs-pecdm/issues) and [Pull Request](https://github.com/puppetlabs/puppetlabs-pecdm/pulls) components of the GitHub project. 30 | 31 | The project is under active development and yet to release an initial version. There is no guarantee yet on a stable interface from commit to commit and those commits may include breaking changes. 32 | 33 | ## Setup 34 | 35 | ### What pecdm affects 36 | 37 | Types of things you'll be paying your cloud provider for 38 | 39 | * Instances of various sizes 40 | * Load balancers 41 | * Networks 42 | 43 | ### Setup Requirements 44 | 45 | #### Deploying upon GCP 46 | * [GCP Cloud SDK Intalled](https://cloud.google.com/sdk/docs/quickstarts) 47 | * [GCP Application Default Credentials](https://cloud.google.com/sdk/gcloud/reference/auth/application-default/) 48 | 49 | #### Deploying upon AWS 50 | * [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) 51 | * [Environment variables or Shared Credentials file Authentication Method](https://www.terraform.io/docs/providers/aws/index.html#authentication) 52 | * [If using MFA, a script to set environment variables](scripts/aws_bastion_mfa_export.sh) 53 | 54 | #### Deploying upon Azure 55 | * [Install the Azure CLI](https://docs.microsoft.com/en-us/cli/azure/install-azure-cli) 56 | * [AZ Login](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs#authenticating-to-azure) 57 | 58 | #### Common Requirements 59 | * [Bolt Installed](https://puppet.com/docs/bolt/latest/bolt_installing.html) 60 | * [Git Installed](https://git-scm.com/downloads) 61 | * [Terraform Installed](https://www.terraform.io/downloads.html) 62 | 63 | ### Beginning with pecdm 64 | 65 | 1. Clone this repository: `git clone https://github.com/puppetlabs/puppetlabs-pecdm.git && cd puppetlabs-pecdm` 66 | 2. Install module dependencies: `bolt module install --no-resolve` (manually manages modules to take advantage of functionality that allows for additional content to be deployed that does not adhere to the Puppet Module packaging format, e.g. Terraform modules) 67 | 3. Run plan: `bolt plan run pecdm::provision project=example ssh_user=john.doe firewall_allow='[ "0.0.0.0/0" ]'` 68 | 4. Wait. This is best executed from a bastion host or alternatively, a fast connection with strong upload bandwidth 69 | 70 | ## Usage 71 | 72 | **Parameter requirements** 73 | 74 | * `lb_ip_mode`: Default is **private**, which will result in load balancer creation that is only accessible from within the VPC where PE is provisioned. To access the load balancer your agents will need to reside within the same VPC as PE or have its VPC peered so private IPs can be routed between PE and the agent's VPC. If you set this parameter to **public** on AWS then the ELB creation will associate a public IP, potentially accessible from the internet. On GCP, setting parameter to **public** will result in PE deployment failure due to GCP not providing DNS entires for internet facing load balancers. 75 | 76 | **Windows Native SSH workaround** 77 | 78 | Due to bolt being [unable to authenticate with ed25519 keys over SSH transport on Windows](https://puppet.com/docs/bolt/latest/bolt_known_issues.html#unable-to-authenticate-with-ed25519-keys-over-ssh-transport-on-windows) you must utilize the [Native ssh transport](https://puppet.com/docs/bolt/latest/experimental_features.html#native-ssh-transport) when running pecdm from a Windows workstation. This is done automatically on provisioning if your current project directory lacks an inventory.yaml. If you choose to maintain your own inventory.yaml file than add the below configuration example. 79 | 80 | ``` 81 | ssh: 82 | native-ssh: true 83 | ssh-command: 'ssh' 84 | ``` 85 | 86 | ### Adding user specific deployment requirements 87 | 88 | There are an infinite number of requirements and options for provisioning and deployment to the cloud that each individual has, it is impossible for pecdm or peadm to support them all and they are often not applicable from one user to another so inappropriate to be added to pecdm. To adapt to this need we develop both modules in a way that makes it possible to compose their individual subcomponents into your own specific implementation. 89 | 90 | An example repository which illustrates this composability can be found at [ody/example-pe_provisioner](https://github.com/ody/example-pe_provisioner/tree/main/plans). This Bolt Project models a hypothetical Example organization which is provisioning PE on Google Cloud Platform and uses the individual subplans in pecdm sandwiched around some custom code and a [Terraform module](https://github.com/ody/terraform-example-pe_dns) that will create DNS records for each provisioned component in Cloud DNS using a user owned domain. 91 | 92 | **Example: params.json** 93 | 94 | The command line will likely serve most uses of **pecdm** but if you wish to pass a longer list of IP blocks that are authorized to access your PE stack than creating a **params.json** file is going to be a good idea, instead of trying to type out a multi value array on the command line. The value that will ultimately be set for the GCP firewall will always include the internal network address space to ensure everything works no matter what is passed in by the user. 95 | 96 | ``` 97 | { 98 | "project" : "example", 99 | "ssh_user" : "john.doe", 100 | "version" : "2019.0.4", 101 | "provider" : "google', 102 | "firewall_allow" : [ "71.236.165.233/32", "131.252.0.0/16", 140.211.0.0/16 ] 103 | 104 | } 105 | ``` 106 | 107 | How to execute plan with **params.json**: `bolt plan run pecdm::provision --params @params.json` 108 | 109 | ### Deploying examples 110 | 111 | #### Deploy standard architecture on AWS with the developer role using a MFA enabled user 112 | 113 | This can also be used to deploy PE's large architecture without a fail over replica on AWS 114 | 115 | ``` 116 | source scripts/aws_bastion_mfa_export.sh -p development 117 | 118 | bolt plan run pecdm provider=aws architecture=standard 119 | ``` 120 | 121 | Please note that for bolt to authenticate to the AWS-provisioned VMs you need to enable ssh agent like so: 122 | 123 | ```bash 124 | $ eval `ssh-agent` 125 | $ ssh-add 126 | ``` 127 | 128 | For more information about setting environment variables, please take a look at the detailed instructions on the [scripts/README](scripts/README.md) file 129 | 130 | ### Destroying examples 131 | 132 | #### Destroy GCP stack 133 | 134 | The number of options required are reduced when destroying a stack 135 | 136 | `bolt plan run pecdm::destroy provider=google` 137 | 138 | #### Destroy AWS stack 139 | 140 | The number of options required are reduced when destroying a stack 141 | 142 | `bolt plan run pecdm::destroy provider=aws` 143 | 144 | ### Upgrading examples 145 | 146 | **Upgrade is currently non-functional** 147 | 148 | #### Upgrade a AWS stack 149 | 150 | `bolt plan run pecdm::upgrade provider=aws ssh_user=centos version=2021.1.0` 151 | 152 | ## Limitations 153 | 154 | Only supports what peadm supports and not all supporting Terraform modules have parity with each other. 155 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'bundler' 4 | require 'puppet_litmus/rake_tasks' if Gem.loaded_specs.key? 'puppet_litmus' 5 | require 'puppetlabs_spec_helper/rake_tasks' 6 | require 'puppet-syntax/tasks/puppet-syntax' 7 | require 'github_changelog_generator/task' if Gem.loaded_specs.key? 'github_changelog_generator' 8 | require 'puppet-strings/tasks' if Gem.loaded_specs.key? 'puppet-strings' 9 | 10 | def changelog_user 11 | return unless Rake.application.top_level_tasks.include? "changelog" 12 | returnVal = nil || JSON.load(File.read('metadata.json'))['author'] 13 | raise "unable to find the changelog_user in .sync.yml, or the author in metadata.json" if returnVal.nil? 14 | puts "GitHubChangelogGenerator user:#{returnVal}" 15 | returnVal 16 | end 17 | 18 | def changelog_project 19 | return unless Rake.application.top_level_tasks.include? "changelog" 20 | 21 | returnVal = nil 22 | returnVal ||= begin 23 | metadata_source = JSON.load(File.read('metadata.json'))['source'] 24 | metadata_source_match = metadata_source && metadata_source.match(%r{.*\/([^\/]*?)(?:\.git)?\Z}) 25 | 26 | metadata_source_match && metadata_source_match[1] 27 | end 28 | 29 | raise "unable to find the changelog_project in .sync.yml or calculate it from the source in metadata.json" if returnVal.nil? 30 | 31 | puts "GitHubChangelogGenerator project:#{returnVal}" 32 | returnVal 33 | end 34 | 35 | def changelog_future_release 36 | return unless Rake.application.top_level_tasks.include? "changelog" 37 | returnVal = "v%s" % JSON.load(File.read('metadata.json'))['version'] 38 | raise "unable to find the future_release (version) in metadata.json" if returnVal.nil? 39 | puts "GitHubChangelogGenerator future_release:#{returnVal}" 40 | returnVal 41 | end 42 | 43 | PuppetLint.configuration.send('disable_relative') 44 | 45 | 46 | if Gem.loaded_specs.key? 'github_changelog_generator' 47 | GitHubChangelogGenerator::RakeTask.new :changelog do |config| 48 | raise "Set CHANGELOG_GITHUB_TOKEN environment variable eg 'export CHANGELOG_GITHUB_TOKEN=valid_token_here'" if Rake.application.top_level_tasks.include? "changelog" and ENV['CHANGELOG_GITHUB_TOKEN'].nil? 49 | config.user = "#{changelog_user}" 50 | config.project = "#{changelog_project}" 51 | config.future_release = "#{changelog_future_release}" 52 | config.exclude_labels = ['maintenance'] 53 | config.header = "# Change log\n\nAll notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org)." 54 | config.add_pr_wo_labels = true 55 | config.issues = false 56 | config.merge_prefix = "### UNCATEGORIZED PRS; LABEL THEM ON GITHUB" 57 | config.configure_sections = { 58 | "Changed" => { 59 | "prefix" => "### Changed", 60 | "labels" => ["backwards-incompatible"], 61 | }, 62 | "Added" => { 63 | "prefix" => "### Added", 64 | "labels" => ["enhancement", "feature"], 65 | }, 66 | "Fixed" => { 67 | "prefix" => "### Fixed", 68 | "labels" => ["bug", "documentation", "bugfix"], 69 | }, 70 | } 71 | end 72 | else 73 | desc 'Generate a Changelog from GitHub' 74 | task :changelog do 75 | raise < 1.15' 84 | condition: "Gem::Version.new(RUBY_VERSION.dup) >= Gem::Version.new('2.3.0')" 85 | EOM 86 | end 87 | end 88 | 89 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: 1.1.x.{build} 3 | skip_branch_with_pr: true 4 | branches: 5 | only: 6 | - main 7 | - release 8 | skip_commits: 9 | message: /^\(?doc\)?.*/ 10 | clone_depth: 10 11 | init: 12 | - SET 13 | - 'mkdir C:\ProgramData\PuppetLabs\code && exit 0' 14 | - 'mkdir C:\ProgramData\PuppetLabs\facter && exit 0' 15 | - 'mkdir C:\ProgramData\PuppetLabs\hiera && exit 0' 16 | - 'mkdir C:\ProgramData\PuppetLabs\puppet\var && exit 0' 17 | environment: 18 | matrix: 19 | - 20 | RUBY_VERSION: 25-x64 21 | CHECK: validate lint check rubocop 22 | - 23 | PUPPET_GEM_VERSION: ~> 6.0 24 | RUBY_VERSION: 25 25 | CHECK: parallel_spec 26 | - 27 | PUPPET_GEM_VERSION: ~> 6.0 28 | RUBY_VERSION: 25-x64 29 | CHECK: parallel_spec 30 | matrix: 31 | fast_finish: true 32 | install: 33 | - set PATH=C:\Ruby%RUBY_VERSION%\bin;%PATH% 34 | - bundle install --jobs 4 --retry 2 --without system_tests 35 | - type Gemfile.lock 36 | build: off 37 | test_script: 38 | - bundle exec puppet -V 39 | - ruby -v 40 | - gem -v 41 | - bundle -v 42 | - bundle exec rake %CHECK% 43 | notifications: 44 | - provider: Email 45 | to: 46 | - nobody@nowhere.com 47 | on_build_success: false 48 | on_build_failure: false 49 | on_build_status_changed: false 50 | -------------------------------------------------------------------------------- /bolt-project.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | name: pecdm 3 | -------------------------------------------------------------------------------- /examples/params.json: -------------------------------------------------------------------------------- 1 | { 2 | "project" : "example", 3 | "ssh_user" : "john.doe", 4 | "firewall_allow" : [ "0.0.0.0/0" ] 5 | } 6 | -------------------------------------------------------------------------------- /lib/puppet/functions/pecdm/is_windows.rb: -------------------------------------------------------------------------------- 1 | # Use facter on local machine that initiated pecdm to determine if it is being 2 | # ran on Windows 3 | # 4 | require 'facter' 5 | Puppet::Functions.create_function(:'pecdm::is_windows') do 6 | def is_windows # rubocop:disable Naming/PredicateName 7 | Facter.value('os.name') == 'windows' 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/puppet/functions/pecdm/with_tempfile_containing.rb: -------------------------------------------------------------------------------- 1 | # Creates a temporary file on the local machine which initiated pecdm and cleans 2 | # it up when the code requiring the file completes 3 | # 4 | require 'tempfile' 5 | Puppet::Functions.create_function(:'pecdm::with_tempfile_containing') do 6 | dispatch :with_tempfile_containing do 7 | param 'String', :name 8 | param 'String', :contents 9 | param 'Optional[String]', :extension 10 | block_param 'Callable[1, 1]', :block 11 | end 12 | def with_tempfile_containing(name, contents, extension = nil) 13 | params = if extension 14 | [name, extension] 15 | else 16 | name 17 | end 18 | Tempfile.open(params) do |file| 19 | file.binmode # Stop Ruby implicitly doing CRLF translations and breaking tests 20 | file.write(contents) 21 | file.flush 22 | yield file.path 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "puppetlabs-pecdm", 3 | "version": "0.2.0", 4 | "author": "Cody Herriges", 5 | "summary": "Automatic Puppet Enterprise, a Bolt driven fusion of peadm and teraform", 6 | "license": "Apache-2.0", 7 | "source": "https://github.com/puppetlabs/puppetlabs-pecdm", 8 | "dependencies": [ 9 | { 10 | "name": "puppetlabs-stdlib", 11 | "version_requirement": ">= 8.0.0 < 10.0.0" 12 | } 13 | ], 14 | "operatingsystem_support": [ 15 | { 16 | "operatingsystem": "CentOS", 17 | "operatingsystemrelease": [ 18 | "7" 19 | ] 20 | }, 21 | { 22 | "operatingsystem": "OracleLinux", 23 | "operatingsystemrelease": [ 24 | "7" 25 | ] 26 | }, 27 | { 28 | "operatingsystem": "RedHat", 29 | "operatingsystemrelease": [ 30 | "8" 31 | ] 32 | }, 33 | { 34 | "operatingsystem": "Scientific", 35 | "operatingsystemrelease": [ 36 | "7" 37 | ] 38 | }, 39 | { 40 | "operatingsystem": "Debian", 41 | "operatingsystemrelease": [ 42 | "9" 43 | ] 44 | }, 45 | { 46 | "operatingsystem": "Ubuntu", 47 | "operatingsystemrelease": [ 48 | "18.04" 49 | ] 50 | } 51 | ], 52 | "requirements": [ 53 | { 54 | "name": "puppet", 55 | "version_requirement": ">= 6.0.0 < 9.0.0" 56 | } 57 | ], 58 | "pdk-version": "3.0.0", 59 | "template-url": "pdk-default#main", 60 | "template-ref": "tags/3.0.0-0-g056e50d" 61 | } 62 | -------------------------------------------------------------------------------- /pdk.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | ignore: [] 3 | -------------------------------------------------------------------------------- /pecdm.code-workspace: -------------------------------------------------------------------------------- 1 | { 2 | "folders": [ 3 | { 4 | "path": "." 5 | } 6 | ] 7 | } -------------------------------------------------------------------------------- /plans/destroy.pp: -------------------------------------------------------------------------------- 1 | # @summary Destroy a pecdm provisioned Puppet Enterprise cluster 2 | # 3 | # @param provider 4 | # Which cloud provider that infrastructure will be provisioned into 5 | # 6 | # @param cloud_region 7 | # Which region to provision infrastructure in, if not provided default will 8 | # be determined by provider 9 | # 10 | plan pecdm::destroy( 11 | Enum['google', 'aws', 'azure'] $provider, 12 | Optional[String[1]] $cloud_region = undef 13 | ) { 14 | run_plan('pecdm::subplans::destroy', { 15 | provider => $provider, 16 | cloud_region => $cloud_region 17 | }) 18 | } 19 | -------------------------------------------------------------------------------- /plans/provision.pp: -------------------------------------------------------------------------------- 1 | # @summary Provision infrastructure and deploy Puppet Enterprise in the cloud 2 | # 3 | # @param architecture 4 | # Which of the three supported architectures to provision infrastructure for 5 | # 6 | # @param cluster_profile 7 | # The profile of the cluster provisioned that determines if it is more suited 8 | # for development or production 9 | # 10 | # @param download_mode 11 | # The method peadm will use to transfer the PE installer to each 12 | # infrastructure node 13 | # 14 | # @param version 15 | # Which PE version to install with peadm 16 | # 17 | # @param compiler_count 18 | # Quantity of compilers that are provisioned and deployed in Large and Extra 19 | # Large installations 20 | # 21 | # @param ssh_pub_key_file 22 | # Path to the ssh public key file that will be passed to Terraform for 23 | # granting access to instances over SSH 24 | # 25 | # @param console_password 26 | # Initial admin user console password, if not provided you will be prompted to 27 | # input one or accept an insecure default 28 | # 29 | # @param node_count 30 | # Number of agent nodes to provision and enroll into deployment for testing 31 | # and development 32 | # 33 | # @param instance_image 34 | # The cloud image that is used for new instance provisioning, format differs 35 | # depending on provider 36 | # 37 | # @param windows_node_count 38 | # Number of Windows agent nodes to provision and enroll into deployment for testing 39 | # and development 40 | # 41 | # @param windows_instance_image 42 | # The cloud image that is used for new Windows instance provisioning, format differs 43 | # depending on provider 44 | # 45 | # @param windows_user 46 | # The adminstrative user account created on Windows nodes nodes allowing for WINRM connections 47 | # 48 | # @param windows_password 49 | # The adminstrative user account password on Windows nodes nodes allowing for WINRM connections 50 | # 51 | # @param subnet 52 | # Name or ID of an existing subnet to attach newly provisioned infrastructure 53 | # to 54 | # 55 | # @param subnet_project 56 | # If using the GCP provider, the name of the project which owns the existing 57 | # subnet you are attaching new infrastructure to if it is different than the 58 | # project you are provisioning into 59 | # 60 | # @param disable_lb 61 | # Option to disable load balancer creation for situations where you're 62 | # required to leverage alternate technology than what is provided by the cloud 63 | # provider 64 | # 65 | # @param ssh_ip_mode 66 | # The type of IPv4 address that will be used to gain SSH access to instances 67 | # 68 | # @param lb_ip_mode 69 | # The type of IPv4 address that is used for load balancing, private or public 70 | # 71 | # @param firewall_allow 72 | # IPv4 address subnets that should have access to PE through firewall 73 | # 74 | # @param dns_alt_names 75 | # Any additional DNS Alternative Names that must be assigned to PE 76 | # infrastructure node certificates 77 | # 78 | # @param extra_peadm_params 79 | # The pecdm plan does not expose all parameters available to peadm, if others 80 | # are needed then pass a hash 81 | # 82 | # @param extra_terraform_vars 83 | # The pecdm plan does not expose all variables defined by supporting Terraform 84 | # modules, if others are needed then pass a hash 85 | # 86 | # @param replica 87 | # Set to true to deploy a replica and enable PE DR for any of the three 88 | # supported architectures 89 | # 90 | # @param stage 91 | # Set to true if you want to skip PE deployment and just provision 92 | # infrastructure 93 | # 94 | # @param write_inventory 95 | # Optionally write an inventory.yaml to the current working directory which is 96 | # specific to the provider 97 | # 98 | # @param provider 99 | # Which cloud provider that infrastructure will be provisioned into 100 | # 101 | # @param project 102 | # One of three things depending on the provider used: a GCP project to deploy 103 | # infrastructure into, the name of the Azure resource group that is created 104 | # specifically for new infrastructure, or a simple tag attached to AWS 105 | # instances 106 | # 107 | # @param ssh_user 108 | # User name that will be used for accessing infrastructure over SSH, defaults 109 | # to ec2-user on when using the AWS provider but a value is required on GCp and Azure 110 | # 111 | # @param cloud_region 112 | # Which region to provision infrastructure in, if not provided default will 113 | # be determined by provider 114 | # 115 | # @param native_ssh 116 | # Set this to true if the plan should leverage native SSH instead of Ruby's 117 | # net-ssh library 118 | # 119 | plan pecdm::provision( 120 | Enum['xlarge', 'large', 'standard'] $architecture = 'standard', 121 | Enum['development', 'production', 'user'] $cluster_profile = 'development', 122 | Enum['direct', 'bolthost'] $download_mode = 'direct', 123 | String[1] $version = '2019.8.10', 124 | Integer $compiler_count = 1, 125 | Optional[String[1]] $ssh_pub_key_file = undef, 126 | Optional[String[1]] $console_password = undef, # lint:ignore:140chars Due to a bug with how bolt handles Optional Sensitive this can not be currently use 127 | Optional[Integer] $node_count = undef, 128 | Optional[Variant[String[1],Hash]] $instance_image = undef, 129 | Optional[Integer] $windows_node_count = undef, 130 | Optional[Variant[String[1],Hash]] $windows_instance_image = undef, 131 | Optional[String[1]] $windows_password = undef, 132 | Optional[String[1]] $windows_user = undef, 133 | Optional[Variant[String[1],Array[String[1]]]] $subnet = undef, 134 | Optional[String[1]] $subnet_project = undef, 135 | Optional[Boolean] $disable_lb = undef, 136 | Enum['private', 'public'] $ssh_ip_mode = 'public', 137 | Enum['private', 'public'] $lb_ip_mode = 'private', 138 | Array $firewall_allow = [], 139 | Array $dns_alt_names = [], 140 | Hash $extra_peadm_params = {}, 141 | Hash $extra_terraform_vars = {}, 142 | Boolean $replica = false, 143 | Boolean $stage = false, 144 | Boolean $write_inventory = true, 145 | Boolean $native_ssh = pecdm::is_windows(), 146 | # The final three parameters depend on the value of $provider, to do magic 147 | Enum['google', 'aws', 'azure'] $provider, 148 | Optional[String[1]] $project = undef, 149 | Optional[String[1]] $ssh_user = undef, 150 | Optional[String[1]] $cloud_region = undef 151 | ) { 152 | if $node_count and $lb_ip_mode != 'private' { 153 | fail_plan('The provisioning of agent nodes requires lb_ip_mode to be set to private') 154 | } 155 | 156 | if $console_password { 157 | $_console_password = $console_password 158 | } else { 159 | $_console_password = prompt('Input Puppet Enterprise console password now or accept default. [puppetLabs123!]', 160 | 'sensitive' => true, 'default' => 'puppetLabs123!' 161 | ) 162 | } 163 | 164 | if $windows_node_count { 165 | if $windows_password { 166 | $_windows_password = Sensitive($windows_password) 167 | } else { 168 | $_windows_password = prompt('Input Windows Node password or accept default. [Pupp3tL@b5P0rtl@nd!]', 169 | 'sensitive' => true, 'default' => 'Pupp3tL@b5P0rtl@nd!' 170 | ) 171 | } 172 | } else { 173 | $_windows_password = undef # prevents unknown variable errors when not provisioning Windows Agents 174 | if $windows_password { 175 | out::message('Windows node password reset to undef because no Windows Agents are to be provisioned') 176 | } 177 | } 178 | 179 | $provisioned = run_plan('pecdm::subplans::provision', { 180 | architecture => $architecture, 181 | cluster_profile => $cluster_profile, 182 | compiler_count => $compiler_count, 183 | ssh_pub_key_file => $ssh_pub_key_file, 184 | node_count => $node_count, 185 | instance_image => $instance_image, 186 | windows_node_count => $windows_node_count, 187 | windows_instance_image => $windows_instance_image, 188 | subnet => $subnet, 189 | subnet_project => $subnet_project, 190 | disable_lb => $disable_lb, 191 | ssh_ip_mode => $ssh_ip_mode, 192 | lb_ip_mode => $lb_ip_mode, 193 | firewall_allow => $firewall_allow, 194 | replica => $replica, 195 | provider => $provider, 196 | project => $project, 197 | ssh_user => $ssh_user, 198 | windows_user => $windows_user, 199 | windows_password => $_windows_password, 200 | cloud_region => $cloud_region, 201 | native_ssh => $native_ssh, 202 | extra_terraform_vars => $extra_terraform_vars 203 | }) 204 | 205 | # Show provisioning results in verbose mode 206 | out::verbose("pecdm::provision provisioned:\n\n${provisioned.stdlib::to_json_pretty}\n") 207 | 208 | unless $stage { 209 | run_plan('pecdm::subplans::deploy', { 210 | inventory => $provisioned['pe_inventory'], 211 | compiler_pool_address => $provisioned['compiler_pool_address'], 212 | download_mode => $download_mode, 213 | version => $version, 214 | console_password => $_console_password.unwrap, 215 | dns_alt_names => $dns_alt_names, 216 | extra_peadm_params => $extra_peadm_params 217 | }) 218 | 219 | if $node_count { 220 | $agent_targets = get_targets(getvar('provisioned.agent_inventory').map |$target| { 221 | $target['name'] 222 | }.flatten()) 223 | } 224 | if $windows_node_count { 225 | $windows_agent_targets = get_targets(getvar('provisioned.windows_agent_inventory').map |$target| { 226 | $target['name'] 227 | }.flatten()) 228 | } else { 229 | $windows_agent_targets = [] 230 | } 231 | if $node_count or $windows_node_count { 232 | run_plan('pecdm::utils::deploy_agents', $agent_targets + $windows_agent_targets, { 233 | primary_host => getvar('provisioned.pe_inventory.server.0.name'), 234 | compiler_pool_address => $provisioned['compiler_pool_address'], 235 | }) 236 | } 237 | } else { 238 | out::message('The parameter stage was set to true, infrastructure has been staged but Puppet Enterprise deployment skipped') 239 | } 240 | 241 | if $write_inventory { 242 | run_plan('pecdm::utils::inventory_yaml', { 243 | provider => $provider, 244 | ssh_ip_mode => $ssh_ip_mode, 245 | native_ssh => $native_ssh, 246 | }) 247 | } 248 | } 249 | -------------------------------------------------------------------------------- /plans/subplans/deploy.pp: -------------------------------------------------------------------------------- 1 | # @summary Deploy Puppet Enterprise in the cloud by launching peadm with appropriate parameters 2 | # 3 | # @param inventory 4 | # Node inventory hash provided by Pecdm::Subplans::Provision plan 5 | # 6 | # @param compiler_pool_address 7 | # The FQDN that agent nodes will connect to for catalog compilation services 8 | # 9 | # @param download_mode 10 | # The method peadm will use to transfer the PE installer to each 11 | # infrastructure node 12 | # 13 | # @param version 14 | # Which PE version to install with peadm 15 | # 16 | # @param console_password 17 | # Initial admin user console password, if not provided you will be prompted to 18 | # input one or accept an insecure default 19 | # 20 | # @param dns_alt_names 21 | # Any additional DNS Alternative Names that must be assigned to PE 22 | # infrastructure node certificates 23 | # 24 | # @param extra_peadm_params 25 | # The pecdm plan does not expose all parameters available to peadm, if others 26 | # are needed then pass a hash 27 | # 28 | plan pecdm::subplans::deploy( 29 | Hash $inventory, 30 | String[1] $compiler_pool_address, 31 | String[1] $console_password, 32 | Enum['direct', 'bolthost'] $download_mode = 'direct', 33 | String[1] $version = '2019.8.10', 34 | Array $dns_alt_names = [], 35 | Hash $extra_peadm_params = {}, 36 | ) { 37 | out::message('Starting deployment of Puppet Enterprise') 38 | 39 | $peadm_target_list = $inventory.map |$_, $v| { 40 | $v.map |$target| { 41 | $target['name'] 42 | } 43 | }.flatten() 44 | 45 | # Generate a parameters list to be fed to puppetlabs/peadm based on which 46 | # architecture we've chosen to deploy. PEAdm will figure out the correct 47 | # thing to do based on whether or not there are valid values for each 48 | # architecture component. An empty array is equivalent to not defining the 49 | # parameter. 50 | $params = { 51 | 'primary_host' => getvar('inventory.server.0.name'), 52 | 'primary_postgresql_host' => getvar('inventory.psql.0.name'), 53 | 'replica_host' => getvar('inventory.server.1.name'), 54 | 'replica_postgresql_host' => getvar('inventory.psql.1.name'), 55 | 'compiler_hosts' => getvar('inventory.compiler', []).map |$c| { $c['name'] }, 56 | 'console_password' => $console_password, 57 | 'dns_alt_names' => peadm::flatten_compact(['puppet', $compiler_pool_address] + $dns_alt_names).delete(''), 58 | 'compiler_pool_address' => $compiler_pool_address, 59 | 'download_mode' => $download_mode, 60 | 'version' => $version, 61 | } 62 | 63 | # TODO: make this print only when user specifies --verbose 64 | $peadm_install_params = $params + $extra_peadm_params 65 | out::verbose("peadm::install params:\n\n${peadm_install_params.stdlib::to_json_pretty}\n") 66 | 67 | wait_until_available(get_targets($peadm_target_list), wait_time => 300) 68 | 69 | # Once all the infrastructure data has been collected, handoff to puppetlabs/peadm 70 | run_plan('peadm::install', $params + $extra_peadm_params) 71 | 72 | $console = getvar('inventory.server.0.uri') 73 | out::message('Finished deployment of Puppet Enterprise') 74 | out::message("Log into Puppet Enterprise Console: https://${console}") 75 | } 76 | -------------------------------------------------------------------------------- /plans/subplans/destroy.pp: -------------------------------------------------------------------------------- 1 | # @summary Destroy a pecdm provisioned Puppet Enterprise cluster 2 | # 3 | # @param provider 4 | # Which cloud provider that infrastructure will be provisioned into 5 | # 6 | # @param cloud_region 7 | # Which region to provision infrastructure in, if not provided default will 8 | # be determined by provider 9 | # 10 | plan pecdm::subplans::destroy( 11 | Enum['google', 'aws', 'azure'] $provider, 12 | String[1] $cloud_region = $provider ? { 'azure' => 'westus2' ,'aws' => 'us-west-2', default => 'us-west1' } 13 | ) { 14 | out::message("Destroying Puppet Enterprise deployment on ${provider}") 15 | 16 | $tf_dir = ".terraform/${provider}_pe_arch" 17 | 18 | # Ensure the Terraform project directory has been initialized ahead of 19 | # attempting a destroy 20 | run_task('terraform::initialize', 'localhost', dir => $tf_dir) 21 | 22 | $vars_template = @(TFVARS) 23 | <% unless $cloud_region == undef { -%> 24 | region = "<%= $cloud_region %>" 25 | <% } -%> 26 | <% if $provider == 'google' { -%> 27 | destroy = true 28 | <% } -%> 29 | # Required parameters which values are irrelevant on destroy 30 | project = "oppenheimer" 31 | user = "oppenheimer" 32 | windows_user = "oppenheimer" 33 | windows_password = "oppenheimer" 34 | |TFVARS 35 | 36 | $tfvars = inline_epp($vars_template) 37 | 38 | pecdm::with_tempfile_containing('', $tfvars, '.tfvars') |$tfvars_file| { 39 | # Stands up our cloud infrastructure that we'll install PE onto, returning a 40 | # specific set of data via TF outputs that if replicated will make this plan 41 | # easily adaptable for use with multiple cloud providers 42 | run_plan('terraform::destroy', 43 | dir => $tf_dir, 44 | var_file => $tfvars_file 45 | ) 46 | } 47 | 48 | out::message('Puppet Enterprise deployment successfully destroyed') 49 | } 50 | -------------------------------------------------------------------------------- /plans/subplans/provision.pp: -------------------------------------------------------------------------------- 1 | # @summary Provision required infrastructure in the cloud to support a specific Puppet Enterprise architecture 2 | # 3 | # @param architecture 4 | # Which of the three supported architectures to provision infrastructure for 5 | # 6 | # @param cluster_profile 7 | # The profile of the cluster provisioned that determines if it is more suited 8 | # for development or production 9 | # 10 | # @param compiler_count 11 | # Quantity of compilers that are provisioned and deployed in Large and Extra 12 | # Large installations 13 | # 14 | # @param ssh_pub_key_file 15 | # Path to the ssh public key file that will be passed to Terraform for 16 | # granting access to instances over SSH 17 | # 18 | # @param node_count 19 | # Number of Linux agent nodes to provision and enroll into deployment for testing 20 | # and development 21 | # 22 | # @param instance_image 23 | # The cloud image that is used for new instance provisioning, format differs 24 | # depending on provider 25 | # 26 | # @param windows_node_count 27 | # Number of Windows agent nodes to provision and enroll into deployment for testing 28 | # and development 29 | # 30 | # @param windows_instance_image 31 | # The cloud image that is used for new Windows instance provisioning, format differs 32 | # depending on provider 33 | # 34 | # @param windows_user 35 | # The adminstrative user account created on Windows nodes nodes allowing for WINRM connections 36 | # 37 | # @param windows_password 38 | # The adminstrative user account password on Windows nodes nodes allowing for WINRM connections 39 | # 40 | # @param subnet 41 | # Name or ID of an existing subnet to attach newly provisioned infrastructure 42 | # to 43 | # 44 | # @param subnet_project 45 | # If using the GCP provider, the name of the project which owns the existing 46 | # subnet you are attaching new infrastructure to if it is different than the 47 | # project you are provisioning into 48 | # 49 | # @param disable_lb 50 | # Option to disable load balancer creation for situations where you're 51 | # required to leverage alternate technology than what is provided by the cloud 52 | # provider 53 | # 54 | # @param ssh_ip_mode 55 | # The type of IPv4 address that will be used to gain SSH access to instances 56 | # 57 | # @param lb_ip_mode 58 | # The type of IPv4 address that is used for load balancing, private or public 59 | # 60 | # @param firewall_allow 61 | # IPv4 address subnets that should have access to PE through firewall 62 | # 63 | # @param extra_terraform_vars 64 | # The pecdm plan does not expose all variables defined by supporting Terraform 65 | # modules, if others are needed then pass a hash 66 | # 67 | # @param replica 68 | # Set to true to deploy a replica and enable PE DR for any of the three 69 | # supported architectures 70 | # 71 | # @param provider 72 | # Which cloud provider that infrastructure will be provisioned into 73 | # 74 | # @param project 75 | # One of three things depending on the provider used: a GCP project to deploy 76 | # infrastructure into, the name of the Azure resource group that is created 77 | # specifically for new infrastructure, or a simple tag attached to AWS 78 | # instances 79 | # 80 | # @param ssh_user 81 | # User name that will be used for accessing infrastructure over SSH, defaults 82 | # to ec2-user on when using the AWS provider but a value is required on GCp and Azure 83 | # 84 | # @param cloud_region 85 | # Which region to provision infrastructure in, if not provided default will 86 | # be determined by provider 87 | # 88 | # @param native_ssh 89 | # Set this to true if the plan should leverage native SSH instead of Ruby's 90 | # net-ssh library 91 | # 92 | plan pecdm::subplans::provision( 93 | Enum['xlarge', 'large', 'standard'] $architecture = 'standard', 94 | Enum['development', 'production', 'user'] $cluster_profile = 'development', 95 | Integer $compiler_count = 1, 96 | Optional[String[1]] $ssh_pub_key_file = undef, 97 | Optional[Integer] $node_count = undef, 98 | Optional[Variant[String[1],Hash]] $instance_image = undef, 99 | Optional[Integer] $windows_node_count = undef, 100 | Optional[Variant[String[1],Hash]] $windows_instance_image = undef, 101 | Optional[Sensitive[String[1]]] $windows_password = undef, 102 | Optional[Variant[String[1],Array[String[1]]]] $subnet = undef, 103 | Optional[String[1]] $subnet_project = undef, 104 | Optional[Boolean] $disable_lb = undef, 105 | Enum['private', 'public'] $ssh_ip_mode = 'public', 106 | Enum['private', 'public'] $lb_ip_mode = 'private', 107 | Array $firewall_allow = [], 108 | Hash $extra_terraform_vars = {}, 109 | Boolean $replica = false, 110 | Boolean $native_ssh = false, 111 | # The final three parameters depend on the value of $provider, to do magic 112 | Enum['google', 'aws', 'azure'] $provider, 113 | String[1] $project = $provider ? { 'aws' => 'pecdm', default => undef }, 114 | String[1] $ssh_user = $provider ? { 'aws' => 'ec2-user', default => undef }, 115 | Optional[String[1]] $windows_user = $provider ? { 'azure' => 'windows', 'aws' => undef, 'google' => undef }, # lint:ignore:140chars 116 | String[1] $cloud_region = $provider ? { 'azure' => 'westus2', 'aws' => 'us-west-2', default => 'us-west1' } # lint:ignore:140chars 117 | ) { 118 | if $provider == 'google' { 119 | if $subnet.is_a(Array) { 120 | fail_plan('Google subnet must be provided as a String, an Array of subnets is only applicable for AWS based deployments') 121 | } 122 | if $lb_ip_mode == 'public' { 123 | fail_plan('Setting lb_ip_mode parameter to public with the GCP provider is not currently supported due to lack of GCP provided DNS') 124 | } 125 | if false in [$windows_instance_image.is_a(Undef), $windows_node_count.is_a(Undef), $windows_password.is_a(Undef), $windows_user.is_a(Undef)] { # lint:ignore:140chars 126 | fail_plan("Provider ${provider} does not support provisioning Windows Agent nodes") 127 | } 128 | 129 | $_instance_image = $instance_image 130 | $_windows_instance_image = undef # While provider doesn't support Windows Agent nodes, ensure this is always undef 131 | } 132 | 133 | if $provider == 'aws' { 134 | if $subnet_project { 135 | fail_plan('Setting subnet_project parameter is only applicable for Google deployments using a subnet shared from another project') 136 | } 137 | 138 | if false in [$windows_instance_image.is_a(Undef), $windows_node_count.is_a(Undef), $windows_password.is_a(Undef), $windows_user.is_a(Undef)] { # lint:ignore:140chars 139 | fail_plan("Provider ${provider} does not support provisioning Windows Agent nodes") 140 | } 141 | 142 | $_instance_image = $instance_image 143 | $_windows_instance_image = undef # While provider doesn't support Windows Agent nodes, ensure this is always undef 144 | } 145 | 146 | if $provider == 'azure' { 147 | if $subnet { 148 | fail_plan('Azure provider does not currently support attachment to existing networks') 149 | } 150 | 151 | if $instance_image.is_a(String) { 152 | $_instance_image = { 'instance_image' => $instance_image, 'image_plan' => '' } 153 | } else { 154 | $_instance_image = $instance_image 155 | } 156 | if $windows_instance_image.is_a(String) { 157 | $_windows_instance_image = { 'windows_instance_image' => $windows_instance_image, 'image_plan' => '' } 158 | } else { 159 | $_windows_instance_image = $windows_instance_image 160 | } 161 | } 162 | 163 | # Where r10k deploys our various Terraform modules for each cloud provider 164 | $tf_dir = ".terraform/${provider}_pe_arch" 165 | 166 | # Ensure the Terraform project directory has been initialized ahead of 167 | # attempting an apply 168 | run_task('terraform::initialize', 'localhost', dir => $tf_dir) 169 | 170 | # Constructs a tfvars file to be used by Terraform 171 | $tfvars = epp('pecdm/tfvars.epp', { 172 | project => $project, 173 | user => $ssh_user, 174 | windows_user => $windows_user, 175 | lb_ip_mode => $lb_ip_mode, 176 | ssh_key => $ssh_pub_key_file, 177 | region => $cloud_region, 178 | node_count => $node_count, 179 | instance_image => $_instance_image, 180 | windows_node_count => $windows_node_count, 181 | windows_instance_image => $_windows_instance_image, 182 | windows_password => $windows_password.unwrap, 183 | subnet => $subnet, 184 | subnet_project => $subnet_project, 185 | firewall_allow => $firewall_allow, 186 | architecture => $architecture, 187 | cluster_profile => $cluster_profile, 188 | replica => $replica, 189 | compiler_count => $compiler_count, 190 | disable_lb => $disable_lb, 191 | provider => $provider, 192 | extra_terraform_vars => $extra_terraform_vars 193 | }) 194 | 195 | out::message("Starting infrastructure provisioning for a ${architecture} deployment of Puppet Enterprise") 196 | 197 | # TODO: make this print only when user specifies --verbose 198 | out::verbose(".tfvars file content:\n\n${tfvars}\n") 199 | 200 | # Creating an on-disk tfvars file to be used by Terraform::Apply to avoid a 201 | # shell escaping issue 202 | # 203 | # with_tempfile_containing() custom function suggestion by Cas is brilliant 204 | # for this, works perfectly 205 | $tf_apply = pecdm::with_tempfile_containing('', $tfvars, '.tfvars') |$tfvars_file| { 206 | # Stands up our cloud infrastructure that we'll install PE onto, returning a 207 | # specific set of data via TF outputs that if replicated will make this plan 208 | # easily adaptable for use with multiple cloud providers 209 | run_plan('terraform::apply', 210 | dir => $tf_dir, 211 | return_output => true, 212 | var_file => $tfvars_file, 213 | refresh_state => $provider ? { 214 | 'aws' => true, 215 | default => false, 216 | } 217 | ) 218 | } 219 | 220 | # A pretty basic target config that just ensures we'll SSH into linux hosts 221 | # with a specific user and properly escalate to the root user 222 | $_target_config = { 223 | 'config' => { 224 | 'ssh' => { 225 | 'user' => $ssh_user, 226 | 'host-key-check' => false, 227 | 'run-as' => 'root', 228 | 'tty' => true, 229 | }, 230 | }, 231 | } 232 | 233 | $native_ssh_config = { 234 | 'config' => { 235 | 'ssh' => { 236 | 'native-ssh' => true, 237 | 'ssh-command' => 'ssh', 238 | }, 239 | }, 240 | } 241 | 242 | $target_config = $native_ssh ? { 243 | true => deep_merge($_target_config, $native_ssh_config), 244 | false => $_target_config 245 | } 246 | 247 | $windows_target_config = { 248 | 'config' => { 249 | 'transport' => 'winrm', 250 | 'winrm' => { 251 | 'user' => $windows_user, 252 | 'password' => $windows_password.unwrap, 253 | 'ssl' => false, 254 | 'connect-timeout' => 30, 255 | }, 256 | }, 257 | } 258 | 259 | # Generate an inventory of freshly provisioned nodes using the parameters that 260 | # are appropriate based on which cloud provider we've chosen to use. Utilizes 261 | # different name and uri parameters to allow for the target's SSH address to 262 | # differ from the names used to configure Puppet on the internal interfaces 263 | $inventory = ['server', 'psql', 'compiler', 'node', 'windows_node'].reduce({}) |Hash $memo, String $i| { 264 | $memo + { $i => resolve_references({ 265 | '_plugin' => 'terraform', 266 | 'dir' => $tf_dir, 267 | 'resource_type' => $provider ? { 268 | 'google' => "google_compute_instance.${i}", 269 | 'aws' => "aws_instance.${i}", 270 | 'azure' => $i ? { 271 | 'windows_node' => "azurerm_windows_virtual_machine.${i}", 272 | default => "azurerm_linux_virtual_machine.${i}", 273 | }, 274 | }, 275 | 'target_mapping' => $provider ? { 276 | 'google' => { 277 | 'name' => 'metadata.internalDNS', 278 | 'uri' => $ssh_ip_mode ? { 279 | 'private' => 'network_interface.0.network_ip', 280 | default => 'network_interface.0.access_config.0.nat_ip', 281 | }, 282 | }, 283 | 'aws' => { 284 | 'name' => 'private_dns', 285 | 'uri' => $ssh_ip_mode ? { 286 | 'private' => 'private_ip', 287 | default => 'public_ip', 288 | }, 289 | }, 290 | 'azure' => { 291 | 'name' => 'tags.internalDNS', 292 | 'uri' => $ssh_ip_mode ? { 293 | 'private' => 'private_ip_address', 294 | default => 'public_ip_address', 295 | }, 296 | } 297 | }, 298 | }) 299 | } 300 | } 301 | 302 | # Create Target objects from our previously generated inventory so that calls 303 | # to the get_target(s) function returns appropriate data 304 | $pecdm_targets = $inventory.map |$f, $v| { $v.map |$target| { 305 | if $f == 'windows_node' { 306 | Target.new($target.stdlib::merge($windows_target_config)) 307 | } else { 308 | Target.new($target.stdlib::merge($target_config)) 309 | } 310 | } }.flatten 311 | 312 | $results = { 313 | 'pe_inventory' => $inventory.filter |$type, $values| { ($values.length > 0) and ($type != 'node' and $type != 'windows_node') }, # lint:ignore:140chars 314 | 'agent_inventory' => $inventory['node'], 315 | 'windows_agent_inventory' => $inventory['windows_node'], 316 | 'compiler_pool_address' => $tf_apply['pool']['value'], 317 | } 318 | 319 | out::message("Finished provisioning infrastructure for a ${architecture} deployment of Puppet Enterprise") 320 | return $results 321 | } 322 | -------------------------------------------------------------------------------- /plans/upgrade.pp: -------------------------------------------------------------------------------- 1 | # @summary Upgrade a pecdm provisioned cluster 2 | # 3 | plan pecdm::upgrade( 4 | Peadm::Pe_version $version = '2021.7.2', 5 | Boolean $native_ssh = false, 6 | Enum['private', 'public'] $ssh_ip_mode = 'public', 7 | Optional[Enum['google', 'aws', 'azure']] $provider = undef, 8 | Hash $extra_peadm_params = {}, 9 | String[1] $ssh_user = $provider ? { 'aws' => 'ec2-user', default => undef }, 10 | ) { 11 | Target.new('name' => 'localhost', 'config' => { 'transport' => 'local' }) 12 | 13 | if $provider { 14 | $_provider = $provider 15 | $tf_dir = ".terraform/${_provider}_pe_arch" 16 | } else { 17 | $detected_provider = ['google', 'aws', 'azure'].map |String $provider| { 18 | $tf_dir = ".terraform/${provider}_pe_arch" 19 | $terraform_output = run_task('terraform::output', 'localhost', dir => $tf_dir).first 20 | unless $terraform_output.value.empty { 21 | $provider 22 | } 23 | }.peadm::flatten_compact() 24 | 25 | if $detected_provider.length > 1 { 26 | fail_plan("Provider detection found two active providers, ${detected_provider.join(', ')}; to use the pecdm::upgrade plan, pass the ${provider} parameter to explicitly select one") # lint:ignore:140chars 27 | } 28 | 29 | $_provider = $detected_provider[0] 30 | $tf_dir = ".terraform/${_provider}_pe_arch" 31 | } 32 | 33 | # A pretty basic target config that just ensures we'll SSH into linux hosts 34 | # with a specific user and properly escalate to the root user 35 | $_target_config = { 36 | 'config' => { 37 | 'ssh' => { 38 | 'user' => $ssh_user, 39 | 'host-key-check' => false, 40 | 'run-as' => 'root', 41 | 'tty' => true, 42 | }, 43 | }, 44 | } 45 | 46 | $native_ssh_config = { 47 | 'config' => { 48 | 'ssh' => { 49 | 'native-ssh' => true, 50 | 'ssh-command' => 'ssh', 51 | }, 52 | }, 53 | } 54 | 55 | $target_config = $native_ssh ? { 56 | true => deep_merge($_target_config, $native_ssh_config), 57 | false => $_target_config 58 | } 59 | 60 | # Generate an inventory of freshly provisioned nodes using the parameters that 61 | # are appropriate based on which cloud provider we've chosen to use. Utilizes 62 | # different name and uri parameters to allow for the target's SSH address to 63 | # differ from the names used to configure Puppet on the internal interfaces 64 | $inventory = ['server', 'psql', 'compiler'].reduce({}) |Hash $memo, String $i| { 65 | $memo + { $i => resolve_references({ 66 | '_plugin' => 'terraform', 67 | 'dir' => $tf_dir, 68 | 'resource_type' => $_provider ? { 69 | 'google' => "google_compute_instance.${i}", 70 | 'aws' => "aws_instance.${i}", 71 | 'azure' => "azurerm_linux_virtual_machine.${i}", 72 | }, 73 | 'target_mapping' => $_provider ? { 74 | 'google' => { 75 | 'name' => 'metadata.internalDNS', 76 | 'uri' => $ssh_ip_mode ? { 77 | 'private' => 'network_interface.0.network_ip', 78 | default => 'network_interface.0.access_config.0.nat_ip', 79 | }, 80 | }, 81 | 'aws' => { 82 | 'name' => 'tags.internalDNS', 83 | 'uri' => $ssh_ip_mode ? { 84 | 'private' => 'private_ip', 85 | default => 'public_ip', 86 | }, 87 | }, 88 | 'azure' => { 89 | 'name' => 'tags.internalDNS', 90 | 'uri' => $ssh_ip_mode ? { 91 | 'private' => 'private_ip_address', 92 | default => 'public_ip_address', 93 | }, 94 | } 95 | }, 96 | }) 97 | } 98 | } 99 | 100 | $inventory.each |$k, $v| { $v.each |$target| { 101 | Target.new($target.merge($target_config)).add_to_group('peadm_nodes') 102 | } } 103 | 104 | $peadm_configs = run_task('peadm::get_peadm_config', [ 105 | get_targets([ 106 | getvar('inventory.server.0.name'), 107 | getvar('inventory.server.1.name'), 108 | ].peadm::flatten_compact) 109 | ]) 110 | 111 | if ($peadm_configs.count == 1) or ($peadm_configs[0].value == $peadm_configs[1].value) { 112 | $current_config = $peadm_configs[0].value 113 | } else { 114 | fail_plan('Collected PEADM configs do not match, primary and replica must report equal configurations to upgrade') 115 | } 116 | 117 | $params = { 118 | 'primary_host' => getvar('current_config.params.primary_host'), 119 | 'primary_postgresql_host' => getvar('current_config.params.primary_postgresql_host'), 120 | 'replica_host' => getvar('current_config.params.replica_host'), 121 | 'replica_postgresql_host' => getvar('current_config.params.replica_postgresql_host'), 122 | 'compiler_hosts' => getvar('current_config.params.compilers'), 123 | 'compiler_pool_address' => getvar('current_config.params.compiler_pool_address'), 124 | 'version' => $version, 125 | } 126 | 127 | $peadm_upgrade_params = $params + $extra_peadm_params 128 | 129 | out::verbose("params var content:\n\n${peadm_upgrade_params}\n") 130 | 131 | # Once all the infrastructure data has been collected, peadm takes over 132 | run_plan('peadm::upgrade', $peadm_upgrade_params) 133 | } 134 | -------------------------------------------------------------------------------- /plans/utils/deploy_agents.pp: -------------------------------------------------------------------------------- 1 | # @summary Deploy puppet agent and enroll a set of nodes into pecdm provisioned cluster 2 | # 3 | # @param compiler_pool_address 4 | # The FQDN that agent nodes will connect to for catalog compilation services 5 | # 6 | # @param primary_host 7 | # The target which is the cluster's primary node that is responsible for 8 | # certificate signing 9 | # 10 | plan pecdm::utils::deploy_agents( 11 | TargetSpec $targets, 12 | String $primary_host, 13 | String $compiler_pool_address = $primary_host, 14 | ) { 15 | out::message('Enrolling agent nodes into new Puppet Enterprise deployment') 16 | 17 | parallelize($targets) |$target| { 18 | run_task('peadm::agent_install', $target, 19 | 'server' => $compiler_pool_address, 20 | 'install_flags' => [ 21 | $target.transport ? { 22 | 'winrm' => '-PuppetServiceEnsure', 23 | default => '--puppet-service-ensure' 24 | }, 25 | 'stopped', 26 | "agent:certname=${target.name}", 27 | ], 28 | ) 29 | 30 | run_task('peadm::submit_csr', $target) 31 | } 32 | 33 | run_task('peadm::sign_csr', peadm::get_targets($primary_host, 1), 34 | 'certnames' => $targets.map |$a| { $a.name }, 35 | ) 36 | 37 | run_task('service', $targets, 38 | name => 'puppet', 39 | action => 'start', 40 | ) 41 | } 42 | -------------------------------------------------------------------------------- /plans/utils/inventory_yaml.pp: -------------------------------------------------------------------------------- 1 | # @summary Write to current working directory a provider specific inventory.yaml 2 | # 3 | # @param provider 4 | # Which cloud provider that infrastructure will be provisioned into 5 | # 6 | # @param ssh_ip_mode 7 | # The type of IPv4 address that will be used to gain SSH access to instances 8 | # 9 | # @param native_ssh 10 | # Set this to true if the plan should write an inventory which uses native SSH 11 | # instead of Ruby's net-ssh library 12 | # 13 | plan pecdm::utils::inventory_yaml( 14 | Enum['google', 'aws', 'azure'] $provider, 15 | Enum['private', 'public'] $ssh_ip_mode, 16 | Boolean $native_ssh = false, 17 | ) { 18 | out::message("Writing inventory.yaml for ${provider}") 19 | 20 | file::write('inventory.yaml', epp('pecdm/inventory_yaml.epp', { 21 | provider => $provider, 22 | ssh_ip_mode => $ssh_ip_mode, 23 | native_ssh => $native_ssh 24 | })) 25 | } 26 | -------------------------------------------------------------------------------- /scripts/README.md: -------------------------------------------------------------------------------- 1 | # Assuming roles 2 | 3 | In some organizations, your access to a cloud provider such as AWS (in this particular case) is done via bastion accounts or roles. 4 | 5 | This guide will help you set the config and credentials files in your machine to assume a role via a bash script. This sample script helps you authenticate, switch roles, and set the necessary environment variables. 6 | 7 | ## Steps 8 | 9 | ### Requirements 10 | 11 | * [AWS CLI](https://aws.amazon.com/cli/) 12 | * JQ 13 | * `jq` is a command-line tool for parsing JSON 14 | * If you are on MacOS you can install it via Homebrew: 15 | 16 | ``` 17 | brew install jq 18 | ``` 19 | 20 | * [Here you will find more information to download](https://stedolan.github.io/jq/download/) `jq` for your OS 21 | 22 | ## AWS Config & Credentials 23 | 24 | Make sure to set your AWS account credentials under `~/.aws/credentials`. This script also expects you have added a profile on `~/.aws/config` 25 | 26 | It's worth noticing that you need to have [MFA enabled in your AWS account](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_mfa_enable_virtual.html) in order to assume roles. 27 | 28 | ### ~/.aws/credentials 29 | 30 | You can create new API access keys in the AWS IAM service. 31 | 32 | ``` 33 | [default] 34 | aws_access_key_id = 35 | aws_secret_access_key = 36 | ``` 37 | 38 | ### ~/.aws/config 39 | 40 | Add the details of your profile 41 | 42 | ``` 43 | [default] 44 | region = eu-central-1 45 | 46 | [profile my-custom-profile] 47 | role_arn = arn:aws:iam:::role/ 48 | source_profile = default 49 | mfa_serial = arn:aws:iam:::mfa/ 50 | ``` 51 | 52 | ### Setting the ENV vars 53 | 54 | To generate the environment variables (`AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN`) run the `aws_bastion_mfa_export.sh` bash script, this will ask you to enter a MFA code 55 | 56 | ``` 57 | source aws_bastion_mfa_export.sh -p 58 | ``` 59 | 60 | **Response** 61 | 62 | ``` 63 | |_ Using profile flag: 64 | |_ Requesting identity with profile 65 | Enter MFA code for arn:aws:iam:::mfa/: 66 | { 67 | "UserId": "...", 68 | "Account": "...", 69 | "Arn": "..." 70 | } 71 | exporting AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN 72 | |_ Current token expires at: 2021-06-08T15:34:18+00:00 73 | ``` 74 | 75 | -------------------------------------------------------------------------------- /scripts/aws_bastion_mfa_export.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ "$#" -eq 0 ]; then 4 | printf 'no argument flags provided\n' >&2 5 | exit 1 6 | fi 7 | 8 | while getopts ":p:" flag; do 9 | case "$flag" in 10 | p ) profile=${OPTARG};; 11 | \? ) echo "Invalid option -$OPTARG" 1>&2; exit 1;; 12 | : ) echo "Invalid option -$OPTARG requires argument" 1>&2; exit 1;; 13 | esac 14 | done 15 | 16 | if [ -z ${profile} ]; then 17 | echo "profile is required" >&2; exit 1 18 | fi 19 | 20 | echo "|_ Using profile flag: $profile"; 21 | 22 | # Make sure we have a temporary token 23 | # This will also request your MFA token if needed 24 | echo "|_ Requesting identity with profile $profile" 25 | aws sts get-caller-identity --profile "$profile" 26 | 27 | cachedir=~/.aws/cli/cache 28 | creds_json=$(cat "$cachedir"/*json) 29 | 30 | AWS_ACCESS_KEY_ID=$(echo "$creds_json" | jq -r .Credentials.AccessKeyId) 31 | AWS_SECRET_ACCESS_KEY=$(echo "$creds_json" | jq -r .Credentials.SecretAccessKey) 32 | AWS_SESSION_TOKEN=$(echo "$creds_json" | jq -r .Credentials.SessionToken) 33 | 34 | echo exporting AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN 35 | export AWS_ACCESS_KEY_ID 36 | export AWS_SECRET_ACCESS_KEY 37 | export AWS_SESSION_TOKEN 38 | 39 | exp=$(echo "$creds_json" | jq -r .Credentials.Expiration) 40 | echo "|_ Current token expires at: $exp" 41 | -------------------------------------------------------------------------------- /spec/default_facts.yml: -------------------------------------------------------------------------------- 1 | # Use default_module_facts.yml for module specific facts. 2 | # 3 | # Facts specified here will override the values provided by rspec-puppet-facts. 4 | --- 5 | ipaddress: "172.16.254.254" 6 | ipaddress6: "FE80:0000:0000:0000:AAAA:AAAA:AAAA" 7 | is_pe: false 8 | macaddress: "AA:AA:AA:AA:AA:AA" 9 | -------------------------------------------------------------------------------- /spec/plans/destroy_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'pecdm::destroy' do 4 | include BoltSpec::Plans 5 | 6 | params = { 7 | 'provider' => 'aws', 8 | } 9 | 10 | it 'destroy plan succeeds' do 11 | expect_plan('pecdm::subplans::destroy').be_called_times(1) 12 | expect(run_plan('pecdm::destroy', params)).to be_ok 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /spec/plans/provision_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'pecdm::provision' do 4 | include BoltSpec::Plans 5 | 6 | params = { 7 | 'provider' => 'aws', 8 | 'console_password' => 'puppetLabs123!', 9 | } 10 | 11 | it 'provision plan succeeds' do 12 | allow_any_out_message 13 | allow_any_out_verbose 14 | expect_plan('pecdm::subplans::provision').be_called_times(1) 15 | expect_plan('pecdm::subplans::deploy').be_called_times(1) 16 | expect(run_plan('pecdm::provision', params)).to be_ok 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /spec/plans/upgrade_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'bolt/target' 3 | require 'bolt/inventory' 4 | require 'bolt/plugin' 5 | 6 | #FIXME this test is failing because of the inventory requirement 7 | # 1) pecdm::upgrade upgrade plan succeeds 8 | # Failure/Error: expect(run_plan('pecdm::upgrade', params)).to be_ok 9 | # expected `#, @status="failure">.ok?` to be truthy, got false 10 | # # ./spec/plans/upgrade_spec.rb:37:in `block (2 levels) in 'describe 'pecdm::upgrade' do 11 | 12 | include BoltSpec::Plans 13 | 14 | params = { 15 | 'provider' => 'aws', 16 | } 17 | 18 | let(:target_data) { { 'name' => 'my-target', 'uri' => 'ssh://my-target' } } 19 | # let(:inventory) { instance_double(Bolt::Inventory::Inventory).as_null_object } 20 | let(:inventory) { instance_double(Bolt::Inventory::Inventory) } 21 | let(:target) { Bolt::Target.new('my-target', inventory) } 22 | let(:plugin) { instance_double('Plugin') } 23 | 24 | before :each do 25 | allow(plugin).to receive(:resolve_references).and_return([target_data]) 26 | allow(inventory).to receive(:get_targets).and_return([target]) 27 | allow(inventory).to receive(:targets).and_return([target]) 28 | allow(inventory).to receive(:target_implementation_class).with(no_args).and_return(Bolt::Target) 29 | allow(inventory).to receive(:version).and_return(2) 30 | allow(inventory).to receive(:create_target_from_hash).and_return(target) 31 | allow(inventory).to receive(:plugins).and_return(plugin) 32 | allow(inventory).to receive(:add_to_group) 33 | # allow_any_instance_of(Bolt::PAL::YamlPlan::Evaluator).to receive(:resolve_references).and_return([target]) 34 | Bolt::Logger.configure({ 'console' => { 'level' => 'trace' } }, true) 35 | end 36 | 37 | it 'upgrade plan succeeds' do 38 | puts "Inventory: #{inventory.inspect}" 39 | allow_any_out_message 40 | allow_task('peadm::get_peadm_config').be_called_times(1) 41 | allow_plan('peadm::upgrade').be_called_times(1) 42 | expect(run_plan('pecdm::upgrade', params)).to be_ok 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | RSpec.configure do |c| 4 | c.mock_with :rspec 5 | end 6 | 7 | require 'puppetlabs_spec_helper/module_spec_helper' 8 | require 'rspec-puppet-facts' 9 | 10 | require 'spec_helper_local' if File.file?(File.join(File.dirname(__FILE__), 'spec_helper_local.rb')) 11 | 12 | include RspecPuppetFacts 13 | 14 | default_facts = { 15 | puppetversion: Puppet.version, 16 | facterversion: Facter.version, 17 | } 18 | 19 | default_fact_files = [ 20 | File.expand_path(File.join(File.dirname(__FILE__), 'default_facts.yml')), 21 | File.expand_path(File.join(File.dirname(__FILE__), 'default_module_facts.yml')), 22 | ] 23 | 24 | default_fact_files.each do |f| 25 | next unless File.exist?(f) && File.readable?(f) && File.size?(f) 26 | 27 | begin 28 | default_facts.merge!(YAML.safe_load(File.read(f), permitted_classes: [], permitted_symbols: [], aliases: true)) 29 | rescue StandardError => e 30 | RSpec.configuration.reporter.message "WARNING: Unable to load #{f}: #{e}" 31 | end 32 | end 33 | 34 | # read default_facts and merge them over what is provided by facterdb 35 | default_facts.each do |fact, value| 36 | add_custom_fact fact, value 37 | end 38 | 39 | RSpec.configure do |c| 40 | c.default_facts = default_facts 41 | c.before :each do 42 | # set to strictest setting for testing 43 | # by default Puppet runs at warning level 44 | Puppet.settings[:strict] = :warning 45 | Puppet.settings[:strict_variables] = true 46 | end 47 | c.filter_run_excluding(bolt: true) unless ENV['GEM_BOLT'] 48 | c.after(:suite) do 49 | RSpec::Puppet::Coverage.report!(0) 50 | end 51 | 52 | # Filter backtrace noise 53 | backtrace_exclusion_patterns = [ 54 | %r{spec_helper}, 55 | %r{gems}, 56 | ] 57 | 58 | if c.respond_to?(:backtrace_exclusion_patterns) 59 | c.backtrace_exclusion_patterns = backtrace_exclusion_patterns 60 | elsif c.respond_to?(:backtrace_clean_patterns) 61 | c.backtrace_clean_patterns = backtrace_exclusion_patterns 62 | end 63 | end 64 | 65 | # Ensures that a module is defined 66 | # @param module_name Name of the module 67 | def ensure_module_defined(module_name) 68 | module_name.split('::').reduce(Object) do |last_module, next_module| 69 | last_module.const_set(next_module, Module.new) unless last_module.const_defined?(next_module, false) 70 | last_module.const_get(next_module, false) 71 | end 72 | end 73 | 74 | # 'spec_overrides' from sync.yml will appear below this line 75 | -------------------------------------------------------------------------------- /spec/spec_helper_local.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Load the BoltSpec library 4 | require 'bolt_spec/plans' 5 | 6 | # Configure Puppet and Bolt for testing 7 | BoltSpec::Plans.init 8 | 9 | # This environment variable can be read by Ruby Bolt tasks to prevent unwanted 10 | # auto-execution, enabling easy unit testing. 11 | ENV['RSPEC_UNIT_TEST_MODE'] ||= 'TRUE' 12 | -------------------------------------------------------------------------------- /templates/inventory_yaml.epp: -------------------------------------------------------------------------------- 1 | <%- | Enum['private', 'public'] $ssh_ip_mode, 2 | Enum['google', 'aws', 'azure'] $provider, 3 | Boolean $native_ssh = false, 4 | | -%> 5 | <%- case $provider { -%> 6 | <%- 'google': { -%> 7 | <%- $uri_param = $ssh_ip_mode ? { -%> 8 | <%- 'private' => 'network_interface.0.network_ip', -%> 9 | <%- 'public' => 'network_interface.0.access_config.0.nat_ip' -%> 10 | <%- } -%> 11 | <%- } -%> 12 | <%- 'aws': { -%> 13 | <%- $uri_param = $ssh_ip_mode ? { -%> 14 | <%- 'private' => 'private_ip', -%> 15 | <%- 'public' => 'public_ip' -%> 16 | <%- } -%> 17 | <%- } -%> 18 | <%- 'azure': { -%> 19 | <%- $uri_param = $ssh_ip_mode ? { -%> 20 | <%- 'private' => 'private_ip_address', -%> 21 | <%- 'public' => 'public_ip_address' -%> 22 | <%- } -%> 23 | <%- } -%> 24 | <%- } -%> 25 | --- 26 | config: 27 | transport: ssh 28 | ssh: 29 | host-key-check: false 30 | run-as: root 31 | <%- if $native_ssh { -%> 32 | native-ssh: true 33 | ssh-command: 'ssh' 34 | <%- } -%> 35 | groups: 36 | - name: peadm_nodes 37 | targets: 38 | <%- if $provider == 'google' { -%> 39 | - _plugin: terraform 40 | dir: .terraform/google_pe_arch 41 | resource_type: google_compute_instance.server 42 | target_mapping: 43 | name: metadata.internalDNS 44 | uri: <%= $uri_param %> 45 | - _plugin: terraform 46 | dir: .terraform/google_pe_arch 47 | resource_type: google_compute_instance.compiler 48 | target_mapping: 49 | name: metadata.internalDNS 50 | uri: <%= $uri_param %> 51 | - _plugin: terraform 52 | dir: .terraform/google_pe_arch 53 | resource_type: google_compute_instance.psql 54 | target_mapping: 55 | name: metadata.internalDNS 56 | uri: <%= $uri_param %> 57 | <%- } -%> 58 | <%- if $provider == 'aws' { -%> 59 | - _plugin: terraform 60 | dir: .terraform/aws_pe_arch 61 | resource_type: aws_instance.server 62 | target_mapping: 63 | name: tags.internalDNS 64 | uri: <%= $uri_param %> 65 | - _plugin: terraform 66 | dir: .terraform/aws_pe_arch 67 | resource_type: aws_instance.compiler 68 | target_mapping: 69 | name: tags.internalDNS 70 | uri: <%= $uri_param %> 71 | - _plugin: terraform 72 | dir: .terraform/aws_pe_arch 73 | resource_type: aws_instance.psql 74 | target_mapping: 75 | name: tags.internalDNS 76 | uri: <%= $uri_param %> 77 | <%- } -%> 78 | <%- if $provider == 'azure' { -%> 79 | - _plugin: terraform 80 | dir: .terraform/azure_pe_arch 81 | resource_type: azurerm_linux_virtual_machine.server 82 | target_mapping: 83 | name: tags.internalDNS 84 | uri: <%= $uri_param %> 85 | - _plugin: terraform 86 | dir: .terraform/azure_pe_arch 87 | resource_type: azurerm_linux_virtual_machine.compiler 88 | target_mapping: 89 | name: tags.internalDNS 90 | uri: <%= $uri_param %> 91 | - _plugin: terraform 92 | dir: .terraform/azure_pe_arch 93 | resource_type: azurerm_linux_virtual_machine.psql 94 | target_mapping: 95 | name: tags.internalDNS 96 | uri: <%= $uri_param %> 97 | <%- } -%> 98 | - name: agent_nodes 99 | targets: 100 | <%- if $provider == 'google' { -%> 101 | - _plugin: terraform 102 | dir: .terraform/google_pe_arch 103 | resource_type: google_compute_instance.node 104 | target_mapping: 105 | name: metadata.internalDNS 106 | uri: <%= $uri_param %> 107 | <%- } -%> 108 | <%- if $provider == 'aws' { -%> 109 | - _plugin: terraform 110 | dir: .terraform/aws_pe_arch 111 | resource_type: aws_instance.node 112 | target_mapping: 113 | name: public_dns 114 | uri: <%= $uri_param %> 115 | <%- } -%> 116 | <%- if $provider == 'azure' { -%> 117 | - _plugin: terraform 118 | dir: .terraform/azure_pe_arch 119 | resource_type: azurerm_linux_virtual_machine.node 120 | target_mapping: 121 | name: tags.internalDNS 122 | uri: <%= $uri_param %> 123 | <%- } -%> 124 | - name: windows_agent_nodes 125 | targets: 126 | <%- if $provider == 'google' { -%> 127 | - _plugin: terraform 128 | dir: .terraform/google_pe_arch 129 | resource_type: google_compute_instance.windows_node 130 | target_mapping: 131 | name: metadata.internalDNS 132 | uri: <%= $uri_param %> 133 | <%- } -%> 134 | <%- if $provider == 'aws' { -%> 135 | - _plugin: terraform 136 | dir: .terraform/aws_pe_arch 137 | resource_type: aws_instance.windows_node 138 | target_mapping: 139 | name: public_dns 140 | uri: <%= $uri_param %> 141 | <%- } -%> 142 | <%- if $provider == 'azure' { -%> 143 | - _plugin: terraform 144 | dir: .terraform/azure_pe_arch 145 | resource_type: azurerm_windows_virtual_machine.windows_node 146 | target_mapping: 147 | name: tags.internalDNS 148 | uri: <%= $uri_param %> 149 | <%- } -%> 150 | config: 151 | transport: winrm 152 | winrm: 153 | ssl: false 154 | -------------------------------------------------------------------------------- /templates/tfvars.epp: -------------------------------------------------------------------------------- 1 | <%- | String $project, 2 | String $user, 3 | Optional[String[1]] $windows_user, 4 | Optional[String[1]] $windows_password, 5 | String $lb_ip_mode, 6 | Optional[String[1]] $ssh_key, 7 | Optional[Integer] $node_count, 8 | Optional[Integer] $windows_node_count, 9 | Optional[Variant[String[1],Hash]] $instance_image, 10 | Optional[Variant[String[1],Hash]] $windows_instance_image, 11 | Optional[Variant[String[1],Array[String[1]]]] $subnet, 12 | Optional[String[1]] $subnet_project, 13 | Optional[Boolean] $disable_lb, 14 | String $region, 15 | Array $firewall_allow, 16 | String $architecture, 17 | String $cluster_profile, 18 | Boolean $replica, 19 | Integer $compiler_count, 20 | String $provider, 21 | Hash $extra_terraform_vars 22 | | -%> 23 | # Mapping all the plan parameters to their corresponding Terraform vars 24 | # 25 | # Quoting is important in a Terraform vars file so we take care in preserving 26 | # them and converting single quotes to double. 27 | project = "<%= $project %>" 28 | user = "<%= $user %>" 29 | <% unless $windows_user == undef { -%> 30 | windows_user = "<%= $windows_user %>" 31 | <% } -%> 32 | <% unless $windows_password == undef { -%> 33 | windows_password = "<%= $windows_password %>" 34 | <% } -%> 35 | lb_ip_mode = "<%= $lb_ip_mode %>" 36 | <% unless $ssh_key == undef { -%> 37 | ssh_key = "<%= $ssh_key %>" 38 | <% } -%> 39 | region = "<%= $region %>" 40 | compiler_count = <%= $compiler_count %> 41 | <% unless $node_count == undef { -%> 42 | node_count = <%= $node_count %> 43 | <% } -%> 44 | <% unless $windows_node_count == undef { -%> 45 | windows_node_count = <%= $windows_node_count %> 46 | <% } -%> 47 | <% unless $instance_image == undef { -%> 48 | <% if $provider == 'azure' { -%> 49 | instance_image = "<%= $instance_image['instance_image'] %>" 50 | image_plan = "<%= $instance_image['image_plan'] %>" 51 | <% } else { -%> 52 | instance_image = "<%= $instance_image %>" 53 | <% } -%> 54 | <% } -%> 55 | <% unless $windows_instance_image == undef { -%> 56 | <% if $provider == 'azure' { -%> 57 | instance_image = "<%= $windows_instance_image['instance_image'] %>" 58 | image_plan = "<%= $windows_instance_image['image_plan'] %>" 59 | <% } else { -%> 60 | instance_image = "<%= $widnwos_instance_image %>" 61 | <% } -%> 62 | <% } -%> 63 | <% unless $subnet == undef { -%> 64 | <% if $provider == 'google' { -%> 65 | subnet = "<%= $subnet %>" 66 | <% } -%> 67 | <% if $provider == 'aws' { -%> 68 | subnet = <%= String($subnet).regsubst('\'', '"', 'G') %> 69 | <% } -%> 70 | <% } -%> 71 | <% unless $subnet_project == undef { -%> 72 | subnet_project = "<%= $subnet_project %>" 73 | <% } -%> 74 | firewall_allow = <%= String($firewall_allow).regsubst('\'', '"', 'G') %> 75 | architecture = "<%= $architecture %>" 76 | cluster_profile = "<%= $cluster_profile %>" 77 | replica = <%= $replica %> 78 | <% unless $disable_lb == undef { -%> 79 | disable_lb = "<%= $disable_lb %>" 80 | <% } -%> 81 | <%- unless $extra_terraform_vars.empty { -%> 82 | <%- $extra_terraform_vars.each | String $key, $value | { -%> 83 | <%- if $value =~ String or $value =~ Boolean { -%> 84 | <%= $key %> = "<%= $value %>" 85 | <%- } elsif $value =~ Integer { -%> 86 | <%= $key %> = <%= $value %> 87 | <%- } elsif $value =~ Array { -%> 88 | <%= $key %> = <%= String($value).regsubst('\'', '"', 'G') %> 89 | <%- } elsif $value =~ Hash { -%> 90 | <%= $key %> = { 91 | <%- $value.each | String $k, String $v | { -%> 92 | "<%= $k %>" = "<%= $v %>" 93 | <%- } -%> 94 | } 95 | <%- } -%> 96 | <%- } -%> 97 | <%- } -%> 98 | --------------------------------------------------------------------------------