├── .github └── workflows │ └── criteo-cookbooks-ci.yml ├── .gitignore ├── .rubocop.yml ├── CHANGELOG.md ├── Gemfile ├── LICENSE ├── Policyfile.rb ├── README.md ├── Rakefile ├── attributes └── default.rb ├── chefignore ├── kitchen.yml ├── libraries ├── default.rb ├── package_helper.rb ├── v3_helper.rb ├── v4_helper.rb ├── version_helper.rb └── windows_version_helper.rb ├── metadata.rb ├── recipes ├── default.rb ├── ms_dotnet3.rb ├── ms_dotnet4.rb └── regiis.rb ├── resources ├── framework.rb └── reboot.rb ├── spec ├── data │ └── 2019_registry.yml ├── libraries │ ├── default_spec.rb │ ├── package_helper_spec.rb │ ├── version_helper_spec.rb │ └── vx_helper_spec.rb ├── recipes │ ├── default_spec.rb │ ├── ms_dotnet3_spec.rb │ ├── ms_dotnet4_spec.rb │ └── regiis_spec.rb ├── registry_mock.rb ├── resources │ ├── framework_spec.rb │ └── reboot_spec.rb └── spec_helper.rb └── test └── cookbooks └── ms_dotnet-test ├── Berksfile ├── metadata.rb └── recipes ├── framework_install.rb └── reboot.rb /.github/workflows/criteo-cookbooks-ci.yml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow to help you get started with Actions 2 | name: Criteo Cookbooks CI 3 | on: 4 | # Triggers the workflow on push or pull request events but only for the master branch 5 | push: 6 | branches: [ master ] 7 | tags: [ 'v*' ] 8 | pull_request: 9 | branches: [ master ] 10 | 11 | jobs: 12 | cookstyle: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: Check out code 16 | uses: actions/checkout@v2 17 | - uses: ruby/setup-ruby@v1 18 | with: 19 | ruby-version: 2.7.6 20 | bundler-cache: true 21 | - name: Run Cookstyle 22 | run: bundle exec cookstyle --display-cop-names --extra-details 23 | rspec: 24 | runs-on: ubuntu-latest 25 | steps: 26 | - name: Check out code 27 | uses: actions/checkout@v2 28 | - uses: ruby/setup-ruby@v1 29 | with: 30 | ruby-version: 2.7.6 31 | bundler-cache: true 32 | - name: Run RSpec 33 | run: bundle exec rspec 34 | kitchen: 35 | needs: [rspec] 36 | runs-on: ubuntu-latest 37 | strategy: 38 | matrix: 39 | windows_version: ['windows-2012r2', 'windows-2016', 'windows-2019', 'windows-2022'] 40 | # dotnet3 use some hacks that are not available on CI 41 | dotnet_version: ['dotnet4'] 42 | steps: 43 | - uses: shimataro/ssh-key-action@v2 44 | with: 45 | key: ${{secrets.AWS_SSH_PRIVATE_KEY}} 46 | known_hosts: unnecessary 47 | - uses: actions/checkout@v2 48 | - uses: ruby/setup-ruby@v1 49 | with: 50 | ruby-version: 2.7.6 51 | bundler-cache: true 52 | - run: bundle exec kitchen test ${{ matrix.dotnet_version }}-${{ matrix.windows_version }} 53 | env: 54 | AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} 55 | AWS_REGION: ${{ secrets.AWS_REGION }} 56 | AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} 57 | AWS_SSH_KEY_ID: ${{ secrets.AWS_SSH_KEY_ID }} 58 | AWS_SUBNET: ${{ secrets.AWS_SUBNET }} 59 | AWS_SECURITY_GROUP: ${{ secrets.AWS_SECURITY_GROUP }} 60 | supermarket: 61 | runs-on: ubuntu-latest 62 | if: startsWith(github.ref, 'refs/tags/') 63 | needs: [kitchen] 64 | steps: 65 | - uses: actions/checkout@v2 66 | - name: Publish to supermarket 67 | uses: afaundez/chef-supermarket-action@8cdbe1cccbe1ecd8685b2ea8f48780135bae7cee 68 | with: 69 | user: criteo 70 | cookbook: ms_dotnet 71 | category: Utilities 72 | env: 73 | SUPERMARKET_API_KEY: ${{ secrets.SUPERMARKET_API_KEY }} 74 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *# 3 | .#* 4 | \#*# 5 | .*.sw[a-z] 6 | *.un~ 7 | pkg/ 8 | 9 | # Berkshelf 10 | .vagrant 11 | Policyfile.lock.json 12 | 13 | # Bundler 14 | Gemfile.lock 15 | bin/* 16 | .bundle/* 17 | 18 | .kitchen/ 19 | .kitchen.local.yml 20 | 21 | *.pem 22 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | LineLength: 2 | Enabled: no 3 | Lint/EndAlignment: 4 | EnforcedStyleAlignWith: variable 5 | MethodLength: 6 | Enabled: no 7 | Metrics/AbcSize: 8 | Enabled: no 9 | Metrics/BlockLength: 10 | Max: 90 11 | Metrics/ClassLength: 12 | Enabled: no 13 | Metrics/CyclomaticComplexity: 14 | Enabled: no 15 | Metrics/LineLength: 16 | Enabled: no 17 | Metrics/PerceivedComplexity: 18 | Enabled: no 19 | Style/CaseIndentation: 20 | EnforcedStyle: end 21 | IndentOneStep: yes 22 | Style/TrailingCommaInArguments: 23 | EnforcedStyleForMultiline: consistent_comma 24 | Style/TrailingCommaInArrayLiteral: 25 | EnforcedStyleForMultiline: consistent_comma 26 | Style/TrailingCommaInHashLiteral: 27 | EnforcedStyleForMultiline: consistent_comma 28 | Layout/EndOfLine: 29 | Enabled: no -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ms_dotnet CHANGELOG 2 | =================== 3 | 4 | This file is used to list changes made in each version of the ms_dotnet cookbook. 5 | 6 | 6.0.0 7 | ----- 8 | - Drop support for all .NET 2.X versions 9 | - Drop support for all .NET 3.X versions except 3.5.SP1 10 | - Drop support for .NET 4.x versions prior to 4.6.2 11 | - Drop support for Windows prior to 8.1 and Windows Server prior to 2012 12 | - Remove recipe ms_dotnet2 13 | - Add support for Windows 10 and Windows Server 2019 & 2022 14 | 15 | 5.1.0 16 | ----- 17 | - Add trivial support for Windows Server 2019 (thanks KenzoB73) 18 | - Use cookstyle instead of rubocop (thanks jakauppila) 19 | 20 | 5.0.0 21 | ----- 22 | 23 | ### Improvments 24 | - Added support for .NET Framework 4.8 (thanks sbielaw) 25 | - Added support for more recent Windows and Windows Server version 26 | - Dead package links have been replaced 27 | - Better CI and integration tests 28 | 29 | ### Breaking changes 30 | - Requires Chef >= 14.7 for built-in windows package support 31 | - ms\_dotnet\_framework default to raise an error on unsupported version 32 | - Dependency to windows cookbook has been dropped 33 | 34 | 4.2.1 35 | ----- 36 | - [PR 93][pr-93] Fix url for .NET 4.7.2 installer (thanks aae42) 37 | 38 | 4.2.0 39 | ----- 40 | - Add support for .NET 4.7.2 41 | - Fix idempotency of .NET 4.7.1 updates (KB405856) on Windows Server 2008R2 42 | 43 | 4.1.0 44 | ----- 45 | - Fix setup via windows feature for.NET 3.5 & .NET 4 46 | - Add support for .NET 4.7.1 with KB4054856 47 | 48 | 4.0.1 49 | ----- 50 | - [PR 75][pr-75] Fix prerequisites for .NET 4.7 (thanks nadobando) 51 | 52 | 4.0.0 53 | ----- 54 | 55 | ### Improvments 56 | - Fix .NET 4.6.2 on Windows Server 2008R2 & Server 2012 (thanks Taliesin Sisson) 57 | - Properly supports .NET 4 for Windows 10 & Windows Server 2016 58 | - Add support for .NET 4.7 59 | - Leverage version helpers from windows cookbook 60 | - Reboot only for .NET packages (thanks Jakauppila) 61 | - Remove some Chef 13 deprecation warnings 62 | - Enhanced patches & prerequisites system 63 | 64 | ### Breaking changes 65 | - Requires Chef >= 12.6 for proper custom resource definition 66 | - Requires Windows cookbook >= 2.1.0 for Windows version's helpers 67 | 68 | 3.2.1 69 | ----- 70 | - [PR 53][pr-53] olive42 - Fix install requirement check 71 | - [PR 49][pr-49] jugatsu - Improve ms_dotnet_framework example in README 72 | - [PR 46][pr-46] olivierlemasle - Install KB2919442 before KB2919355 for .NET 4.6 73 | - [PR 44][pr-44] smurawski - Fix version detection when none installed 74 | 3.2.0 75 | ----- 76 | - [PR 43][pr-43] smurawski - Migrate framework LWRP to custom resource 77 | 78 | 3.1.1 79 | ----- 80 | - [PR 38][pr-38] Jakauppila - Update .NET 4.6 prerequisites (KB3173424) 81 | 82 | 3.1.0 83 | ----- 84 | - b.courtois - Support Windows Server 2016. 85 | - [PR 35][pr-35] b.courtois - Add ability to perform required reboot when installing prerequisites and patches. 86 | - [PR 34][pr-34] b.courtois - Fix patches feature due to invalid prerequisites variable. 87 | - [PR 31][pr-31] Matasx - Add support for .NET version 4.6.2. 88 | - [PR 31][pr-31] Matasx - Resolve `0x80240017 WU_E_NOT_APPLICABLE` [issue with KB2919442][issue-29]. 89 | 90 | 3.0.0 91 | ----- 92 | 93 | ### Improvments 94 | 95 | * Introduce new framework LWRP to setup a specific .NET Version with all its prerequisites and patches. 96 | * Detection of the current installed version is now way more accurate. 97 | * Setup of .NET 3.5 is working properly. 98 | * Proper support of .NET 4.6 and .NET 4.6.1. 99 | * Packages and sources overriding is attribute driven. 100 | * Attribute structure is now the same for .NET 2, 3.5 and 4. 101 | * Add chefspecs and rspecs tests. 102 | 103 | ### Breaking changes 104 | 105 | Default recipes and attributes structure have been updated to use the new LWRP. 106 | Refers to the README for description and examples of the new attributes. 107 | 108 | 2.6.1 109 | ----- 110 | - kamaradclimber - Define Windows::VersionHelper::ProductType constants only once 111 | 112 | 2.6.0 113 | ----- 114 | - b.courtois - Use version helper everywhere to remove references to Win32::Version 115 | - b.courtois - Add new helper to retrieve windows version info from ohai 116 | - b.courtois - Use NetFx3ServerFeatures instead of NetFx3 for .NET3.5 117 | 118 | 2.5.1 119 | ----- 120 | - b.courtois - Trust windows_feature behavior in recipe ms_dotnet3 121 | 122 | 2.5.0 123 | ----- 124 | - b.courtois - Supports custom source for .Net 3.5 install 125 | - b.courtois - Use travis container-based infrastructure 126 | - b.courtois - Use travis bundler caching 127 | - b.courtois - Don't test agains ruby 1.9.3 anymore 128 | 129 | 2.4.0 130 | ----- 131 | - Y.Siu - Support .Net3.5 on Windows 8.1 and Server 2012R2. 132 | 133 | 2.3.0 134 | ----- 135 | - b.courtois - Register aspnet to iis once and only if ISS is present. 136 | 137 | 2.2.1 138 | ----- 139 | - minkaotic - Fix .NET 3.5 install guard clause. 140 | 141 | 2.2.0 142 | ----- 143 | - b.courtois - Do not use the windows_reboot resource and its request action. 144 | 145 | 2.1.1 146 | ----- 147 | - b.courtois - Update constraint to leverage windows cookbook >=1.36.1 148 | 149 | 2.1.0 150 | ----- 151 | - b.courtois - Fix .NET4.5 support on windows 7/Server 2008R2 152 | 153 | 2.0.0 154 | ----- 155 | - b.courtois - Fail chef run when an invalid .NET4 version is specified 156 | - b.courtois - Better support of recents windows version for .NET4 157 | - b.courtois - Add ms_dotnet3 recipe 158 | - b.courtois - Fix ms_dotnet2 recipe and stop to use ms_dotnet2 attributes 159 | 160 | 1.2.0 161 | ----- 162 | - b.courtois - Fail chef run when an invalid .NET4 version is specified 163 | 164 | 1.1.0 165 | ----- 166 | - b.courtois - Fix attributes computation 167 | 168 | 1.0.0 169 | ----- 170 | - b.courtois - Merge recipes ms_dotnet4 and ms_dotnet45 171 | - b.courtois - Add basic support for Server 2012 & 2012R2 172 | 173 | 0.2.0 174 | ----- 175 | - b.courtois - include default recipe on msdotnet2 core install 176 | 177 | 0.1.0 178 | ----- 179 | - j.mauro - Initial release of ms_dotnet 180 | 181 | [issue-29]: https://github.com/criteo-cookbooks/ms_dotnet/issues/29 182 | [pr-31]: https://github.com/criteo-cookbooks/ms_dotnet/pull/31 183 | [pr-34]: https://github.com/criteo-cookbooks/ms_dotnet/pull/34 184 | [pr-35]: https://github.com/criteo-cookbooks/ms_dotnet/pull/35 185 | [pr-38]: https://github.com/criteo-cookbooks/ms_dotnet/pull/38 186 | [pr-43]: https://github.com/criteo-cookbooks/ms_dotnet/pull/43 187 | [pr-44]: https://github.com/criteo-cookbooks/ms_dotnet/pull/44 188 | [pr-46]: https://github.com/criteo-cookbooks/ms_dotnet/pull/46 189 | [pr-49]: https://github.com/criteo-cookbooks/ms_dotnet/pull/49 190 | [pr-53]: https://github.com/criteo-cookbooks/ms_dotnet/pull/53 191 | [pr-75]: https://github.com/criteo-cookbooks/ms_dotnet/pull/75 192 | [pr-93]: https://github.com/criteo-cookbooks/ms_dotnet/pull/93 193 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | source 'https://rubygems.org' 4 | 5 | group :unit_test do 6 | gem 'chef', '= 14.7.17' 7 | gem 'chefspec', '>= 9.2.1' 8 | gem 'fauxhai-ng', '>= 9.3.0' 9 | gem 'rspec', '>= 3.11.0' 10 | end 11 | 12 | group :integration do 13 | gem 'kitchen-ec2', '>= 3.12.0' 14 | gem 'kitchen-inspec', '>= 2.5.2' 15 | gem 'test-kitchen', '>= 3.2.2' 16 | end 17 | 18 | group :lint do 19 | gem 'cookstyle', '= 7.32.1' 20 | end 21 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /Policyfile.rb: -------------------------------------------------------------------------------- 1 | name 'ms_dotnet' 2 | 3 | run_list ['ms_dotnet'] 4 | 5 | cookbook 'ms_dotnet', path: '.' 6 | cookbook 'ms_dotnet-test', path: 'test/cookbooks/ms_dotnet-test' 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ms_dotnet Cookbook 2 | ================== 3 | [![Cookbook Version][cookbook_version]][cookbook] 4 | [![Build Status][build_badge]][build_status] 5 | [![License][license]][license] 6 | 7 | Install the Microsoft .NET Framework. 8 | 9 | Requirements 10 | ------------ 11 | This cookbook supports and requires Chef 14.7+ to take advantage of the `reboot` and `windows_package` built-in resources. 12 | 13 | ### Platforms 14 | * Windows 8.1 15 | * Windows 10 16 | * Windows Server 2012 (R1, R2) 17 | * Windows Server 2016 18 | * Windows Server 2019 19 | * Windows Server 2022 20 | 21 | Known Issues 22 | ------------ 23 | Here are the known issues you can encounter with ms_dotnet recipes: 24 | * "Access denied" error on windows_package when running chef via WinRM 25 | * `Cause`: winrm limitation 26 | * `Common environment`: knife windows bootstrap, chef-provisioning, test-kitchen 27 | * `Theoretical solution`: try to simulate a local session by wrapping your chef-client call in psexec or a schedule task. 28 | * `Test-Kitchen solution`: with v1.8.0 you can use [the winrm-elevated feature][winrm_elevated] that'll run chef via a schedule task 29 | 30 | Usage 31 | ----- 32 | This cookbook provides you two methods to install the major versions of .NET framework. 33 | 34 | ### Common case - Recipes 35 | In common cases you can use the attributes driven recipes provided by this cookbook to setup .NET. 36 | ### Custom case - Resources 37 | In more custom cases, you might need to control in your own cookbook which .NET version should be installed and when the setup should be performed. 38 | You just need to use the `ms_dotnet_framework` custom resource which handles for you the setup mode - feature vs package - and may also install all known patches. 39 | 40 | Resource/Provider 41 | ----------------- 42 | ### ms_dotnet_framework 43 | #### Actions 44 | * `:install` - Install a specific .NET framework 45 | 46 | #### Attribute Parameters 47 | * `version` - Name attribute. Specify the .NET version to install. 48 | * `include_patches` - Determine whether patches should also be applied (default: `true`) 49 | * `feature_source` - Specify custom source for windows features. Only avaiable on NT Version 6.2 (Windows 8/2012) or newer. (default: `nil`) 50 | * `perform_reboot` - Determine whether chef should perform required reboot after installing new packages or feature. (default: `false`) 51 | * `package_sources` - Specify custom sources URL for windows packages. URL are indexed by their content SHA256 checkum. (default: `{}`) 52 | * `require_support` - Determine whether chef should fail when given version is not supported on the current OS (default: `false`) 53 | 54 | > NB: `feature_source` works only on NT Version 6.2 (Windows 8/2012) or newer. 55 | 56 | #### Examples 57 | Install .NET 4.8 from custom sources 58 | 59 | ```ruby 60 | ms_dotnet_framework '4.8' do 61 | action :install 62 | include_patches true 63 | perform_reboot true 64 | package_sources('68c9986a8dcc0214d909aa1f31bee9fb5461bb839edca996a75b08ddffc1483f' => 'http://my-own.site.com/ndp48-x86-x64-allos-enu.exe') 65 | require_support true 66 | end 67 | ``` 68 | 69 | Attributes 70 | ---------- 71 | This cookbook is mostly attributes driven, default values should be enough for common cases. 72 | > NB: because this cookbook is usefull only for windows, attributes are not available on other platforms 73 | 74 | ### Common attributes 75 | Below attributes are accessible via `node['ms_dotnet']['ATTRIBUTE_NAME']` and are common to all recipes: 76 | * `timeout` - Control the execution timeout in seconds of .NET setup packages (default: `600`) 77 | 78 | ### .NET recipes attributes 79 | Recipes `ms_dotnet3` and `ms_dotnet4` are controlled by the following attributes accessible via `node['ms_dotnet']['vX']['ATTRIBUTE_NAME']` - where `X` is the .NET major version: 80 | 81 | * `version` - Specify the .NET version to install (default: `3.5.SP1`, `4.0`) 82 | * `include_patches` - Determine whether patches should also be applied (default: `true`) 83 | * `feature_source` - Specify custom source for windows features. Only avaliable on NT Version 6.2 (Windows 8/2012) or newer. (default: `nil`) 84 | * `perform_reboot` - Determine whether chef should perform required reboot after installing new packages or feature. (default: `false`) 85 | * `package_sources` - Specify custom sources URL for windows packages. URL are indexed by their content SHA256 checkum. (default: `{}`) 86 | * `require_support` - Determine whether chef should fail when given version is not supported on the current OS (default: `false`) 87 | 88 | > NB: `feature_source` works only on NT Version 6.2 (Windows 8/2012) or newer. 89 | 90 | ### Examples 91 | #### Install .NET 3.5 SP1 using a Windows UNC Share 92 | Custom node file to install .NET 3.5 SP1 with no patch on a Windows Server 2012R2, using a Windows share for features binaries: 93 | ```json 94 | { 95 | "name": "my-node.examples.com", 96 | "run_list": [ "recipe[ms_dotnet::ms_dotnet3" ], 97 | "normal": { 98 | "ms_dotnet": { 99 | "v3": { 100 | "include_patches": "false", 101 | "feature_source": "\\ShareSvr\sxs" 102 | } 103 | } 104 | } 105 | } 106 | ``` 107 | 108 | #### Install .NET 4.8 using a package hosted on a custom site 109 | Custom node file to install .NET 4.8 from a custom site: 110 | ```json 111 | { 112 | "name": "my-node.examples.com", 113 | "run_list": [ "recipe[ms_dotnet::ms_dotnet4" ], 114 | "normal": { 115 | "ms_dotnet": { 116 | "v4": { 117 | "version": "4.8", 118 | "package_sources": { 119 | "68c9986a8dcc0214d909aa1f31bee9fb5461bb839edca996a75b08ddffc1483f": "http://my-own.site.com/ndp48-x86-x64-allos-enu.exe" 120 | } 121 | } 122 | } 123 | } 124 | } 125 | ``` 126 | 127 | Recipes 128 | ------- 129 | #### ms_dotnet::default 130 | This recipe does nothing. 131 | 132 | #### ms_dotnet::ms_dotnet3, ms_dotnet::ms_dotnet4 133 | Each of these recipes allow you to install a specific major version of .NET Framework, just by including the recipe in your node `run_list`. 134 | They are attribute driven recipes, please referes to the `Attributes` section of this README to know how to control their behavior. 135 | 136 | ### ms_dotnet::regiis 137 | This recipe register .NET 4 to IIS once and only if IIS service exist. 138 | 139 | Libraries 140 | --------- 141 | ### Default 142 | Provides convenients method for the `ms_dotnet` cookbook. 143 | 144 | #### version_helper 145 | Provides a factory to get specific VersionHelper instance. 146 | 147 | ### PackageHelper 148 | References all official .NET setup & patches packages supported by this cookbook. 149 | 150 | #### packages 151 | Retrieve a Hash containing .NET setup packages info - `name`, `checksum`, `url` & `not_if` guard. 152 | 153 | A string `not_if` guard represents a custom command to determine whether the package should be installed. 154 | A string Array `not_if` guard represents the list of QFE representing or superseding the package. 155 | 156 | #### core? 157 | Determine whether the current node is running on a Core version of Windows. 158 | 159 | #### server? 160 | Determine whether the current node is running on a Server version of Windows. 161 | 162 | #### x64? 163 | Determine whether the architecture of the current node is 64-bits. 164 | 165 | ### VersionHelper 166 | Base abstract class inheriting from `PackageHelper` and providing convenient methods to determine which .NET version the current node supports, and how the setup should be handled. 167 | 168 | #### features 169 | Get windows features required by the given .NET version 170 | 171 | #### installed_version 172 | Get installed .NET version on the current node 173 | 174 | #### package 175 | Get windows package required by the given .NET version 176 | 177 | #### patches 178 | Get windows patches required by the given .NET version 179 | 180 | #### prerequisites 181 | Get windows packages required before installing the given .NET version 182 | 183 | #### supported_versions 184 | Get all .NET versions supported on the current node OS 185 | 186 | ### V3Helper, V4Helper 187 | Subclass of the `VersionHelper`, providing helpers for a specific major version of the .NET Framework. 188 | 189 | Contributing 190 | ------------ 191 | 1. Fork the repository on Github 192 | 2. Create a named feature branch (like `add_component_x`) 193 | 3. Write your change 194 | 4. Write tests for your change (if applicable) 195 | 5. Run the tests, ensuring they all pass 196 | 6. Submit a Pull Request using Github 197 | 198 | License and Authors 199 | ------------------- 200 | Authors: [Baptiste Courtois][annih] (), [Jeremy Mauro][jmauro] () 201 | 202 | ```text 203 | Copyright 2014-2022, Criteo. 204 | 205 | Licensed under the Apache License, Version 2.0 (the "License"); 206 | you may not use this file except in compliance with the License. 207 | You may obtain a copy of the License at 208 | 209 | http://www.apache.org/licenses/LICENSE-2.0 210 | 211 | Unless required by applicable law or agreed to in writing, software 212 | distributed under the License is distributed on an "AS IS" BASIS, 213 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 214 | See the License for the specific language governing permissions and 215 | limitations under the License. 216 | ``` 217 | [annih]: https://github.com/Annih 218 | [jmauro]: https://github.com/jmauro 219 | [repository]: https://github.com/criteo-cookbooks/ms_dotnet 220 | [build_badge]: https://github.com/criteo-cookbooks/ms_dotnet/actions/workflows/criteo-cookbooks-ci.yml/badge.svg?branch=master 221 | [build_status]: https://github.com/criteo-cookbooks/ms_dotnet/actions/workflows/criteo-cookbooks-ci.yml 222 | [cookbook_version]: https://img.shields.io/cookbook/v/ms_dotnet.svg 223 | [cookbook]: https://supermarket.chef.io/cookbooks/ms_dotnet 224 | [license]: https://img.shields.io/github/license/criteo-cookbooks/ms_dotnet.svg 225 | [winrm_elevated]: https://discourse.chef.io/t/released-test-kitchen-1-8-0/8421 226 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'rspec/core/rake_task' 4 | 5 | RSpec::Core::RakeTask.new(:rspec) 6 | 7 | task default: %i(rspec) 8 | -------------------------------------------------------------------------------- /attributes/default.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # 4 | # Cookbook:: ms_dotnet 5 | # Attributes:: default 6 | # Author:: Baptiste Courtois () 7 | # 8 | # Copyright:: (C) 2015-2016, Criteo. 9 | # 10 | # Licensed under the Apache License, Version 2.0 (the "License"); 11 | # you may not use this file except in compliance with the License. 12 | # You may obtain a copy of the License at 13 | # 14 | # http://www.apache.org/licenses/LICENSE-2.0 15 | # 16 | # Unless required by applicable law or agreed to in writing, software 17 | # distributed under the License is distributed on an "AS IS" BASIS, 18 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 19 | # See the License for the specific language governing permissions and 20 | # limitations under the License. 21 | # 22 | return unless platform? 'windows' 23 | 24 | default['ms_dotnet']['timeout'] = 600 25 | default['ms_dotnet']['reboot']['delay'] = 1 26 | 27 | # .NET 3 attributes 28 | default['ms_dotnet']['v3']['version'] = '3.5.SP1' 29 | default['ms_dotnet']['v3']['include_patches'] = true 30 | default['ms_dotnet']['v3']['feature_source'] = nil 31 | default['ms_dotnet']['v3']['perform_reboot'] = false 32 | default['ms_dotnet']['v3']['package_sources'] = {} 33 | default['ms_dotnet']['v3']['require_support'] = false 34 | 35 | # .NET 4 attributes 36 | default['ms_dotnet']['v4']['version'] = '4.6.2' 37 | default['ms_dotnet']['v4']['include_patches'] = true 38 | default['ms_dotnet']['v4']['feature_source'] = nil 39 | default['ms_dotnet']['v4']['perform_reboot'] = false 40 | default['ms_dotnet']['v4']['package_sources'] = {} 41 | default['ms_dotnet']['v4']['require_support'] = false 42 | -------------------------------------------------------------------------------- /chefignore: -------------------------------------------------------------------------------- 1 | # Put files/directories that should be ignored in this file when uploading 2 | # or sharing to the community site. 3 | # Lines that start with '# ' are comments. 4 | 5 | # OS generated files # 6 | ###################### 7 | .DS_Store 8 | Icon? 9 | nohup.out 10 | ehthumbs.db 11 | Thumbs.db 12 | 13 | # SASS # 14 | ######## 15 | .sass-cache 16 | 17 | # EDITORS # 18 | ########### 19 | \#* 20 | .#* 21 | *~ 22 | *.sw[a-z] 23 | *.bak 24 | REVISION 25 | TAGS* 26 | tmtags 27 | *_flymake.* 28 | *_flymake 29 | *.tmproj 30 | .project 31 | .settings 32 | mkmf.log 33 | 34 | ## COMPILED ## 35 | ############## 36 | a.out 37 | *.o 38 | *.pyc 39 | *.so 40 | *.com 41 | *.class 42 | *.dll 43 | *.exe 44 | */rdoc/ 45 | 46 | # Testing # 47 | ########### 48 | .watchr 49 | .rspec 50 | spec/* 51 | spec/fixtures/* 52 | test/* 53 | features/* 54 | Guardfile 55 | Procfile 56 | 57 | # SCM # 58 | ####### 59 | .git 60 | */.git 61 | .gitignore 62 | .gitmodules 63 | .gitconfig 64 | .gitattributes 65 | .svn 66 | */.bzr/* 67 | */.hg/* 68 | */.svn/* 69 | 70 | # Cookbooks # 71 | ############# 72 | CONTRIBUTING 73 | CHANGELOG* 74 | Policyfile.rb 75 | Policyfile.lock.json 76 | 77 | # Kitchen # 78 | ########## 79 | .kitchen 80 | .kitchen.yml 81 | 82 | # Strainer # 83 | ############ 84 | Colanderfile 85 | Strainerfile 86 | .colander 87 | .strainer 88 | 89 | # Vagrant # 90 | ########### 91 | .vagrant 92 | Vagrantfile 93 | -------------------------------------------------------------------------------- /kitchen.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # The following environment variables are required: 3 | # - AWS_ACCESS_KEY_ID 4 | # - AWS_SECRET_ACCESS_KEY 5 | # - AWS_SSH_KEY_ID 6 | # - AWS_REGION 7 | # 8 | # Optional environment variables: 9 | # - AWS_SECURITY_GROUP 10 | # - AWS_SUBNET 11 | # - KITCHEN_NO_CONCURRENCY set it to true if you do not want concurrency 12 | 13 | driver: 14 | name: ec2 15 | instance_type: t3a.large 16 | associate_public_ip: true 17 | iam_profile_name: test-kitchen 18 | retryable_tries: 120 19 | region: <%= ENV['AWS_REGION'] || 'us-west-2' %> 20 | tags: 21 | created-by: <%= ENV['AWS_SSH_KEY_ID'] %> 22 | subnet_filter: 23 | tag: 'Name' 24 | value: <%= ENV['AWS_SUBNET'] || 'chef-testing-opensource-vpc-subnet' %> 25 | security_group_filter: 26 | tag: 'Name' 27 | value: <%= ENV['AWS_SECURITY_GROUP'] || 'chef-testing-opensource-vpc-security-group' %> 28 | block_device_mappings: 29 | - device_name: /dev/sda1 30 | ebs: 31 | volume_type: gp2 32 | delete_on_termination: true 33 | 34 | provisioner: 35 | name: chef_zero 36 | install_strategy: always 37 | chef_license: accept 38 | client_rb: 39 | ssl_verify_mode: :verify_none 40 | verify_api_cert: false 41 | exit_status: ":enabled" 42 | client_fork: false 43 | enforce_path_sanity: true 44 | file_cache_path: c:/temp 45 | retry_on_exit_code: 46 | - 35 47 | max_retries: 2 48 | wait_for_retry: 60 49 | product_name: chef 50 | 51 | transport: 52 | name: winrm 53 | elevated: true 54 | elevated_username: System 55 | elevated_password: null 56 | ssh_key: ~/.ssh/id_rsa # needed to fetch admin password even when using winrm 57 | 58 | platforms: 59 | - name: windows-2012r2 60 | driver: 61 | image_search: 62 | 'owner-id': 801119661308 63 | name: 'Windows_Server-2012-R2-English-STIG-Full-20*' 64 | - name: windows-2016 65 | driver: 66 | image_search: 67 | 'owner-id': 801119661308 68 | name: 'Windows_Server-2016-English-Full-Base-20*' 69 | - name: windows-2019 70 | driver: 71 | image_search: 72 | 'owner-id': 801119661308 73 | name: 'Windows_Server-2019-English-Full-Base-20*' 74 | - name: windows-2022 75 | driver: 76 | image_search: 77 | 'owner-id': 801119661308 78 | name: 'Windows_Server-2022-English-Full-Base-20*' 79 | suites: 80 | - name: dotnet3 81 | run_list: 82 | - recipe[ms_dotnet::ms_dotnet3] 83 | - name: dotnet4 84 | run_list: 85 | - recipe[ms_dotnet::ms_dotnet4] 86 | -------------------------------------------------------------------------------- /libraries/default.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # 4 | # Cookbook:: ms_dotnet 5 | # Library:: default 6 | # Author:: Baptiste Courtois () 7 | # 8 | # Copyright:: (C) 2015-2016, Criteo. 9 | # 10 | # Licensed under the Apache License, Version 2.0 (the "License"); 11 | # you may not use this file except in compliance with the License. 12 | # You may obtain a copy of the License at 13 | # 14 | # http://www.apache.org/licenses/LICENSE-2.0 15 | # 16 | # Unless required by applicable law or agreed to in writing, software 17 | # distributed under the License is distributed on an "AS IS" BASIS, 18 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 19 | # See the License for the specific language governing permissions and 20 | # limitations under the License. 21 | # 22 | require_relative 'v3_helper' 23 | require_relative 'v4_helper' 24 | 25 | # Library module for cookbook ms_dotnet 26 | module MSDotNet 27 | # Factory method to get VersionHelper for a given major .NET version 28 | def self.version_helper(node, major_version) 29 | case major_version 30 | when 3 31 | V3Helper.new node 32 | when 4 33 | V4Helper.new node 34 | else 35 | raise ArgumentError, "Unsupported version '#{major_version}'" 36 | end 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /libraries/package_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # 4 | # Cookbook:: ms_dotnet 5 | # Library:: package_helper 6 | # Author:: Baptiste Courtois () 7 | # 8 | # Copyright:: (C) 2015-2016, Criteo. 9 | # 10 | # Licensed under the Apache License, Version 2.0 (the "License"); 11 | # you may not use this file except in compliance with the License. 12 | # You may obtain a copy of the License at 13 | # 14 | # http://www.apache.org/licenses/LICENSE-2.0 15 | # 16 | # Unless required by applicable law or agreed to in writing, software 17 | # distributed under the License is distributed on an "AS IS" BASIS, 18 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 19 | # See the License for the specific language governing permissions and 20 | # limitations under the License. 21 | # 22 | module MSDotNet 23 | # References the main official .NET setup and patch packages 24 | class PackageHelper 25 | attr_reader :arch, :full_version, :nt_version, :is_core, :is_server, :machine_type 26 | 27 | def initialize(node) 28 | @arch = node['kernel']['machine'] == 'x86_64' ? 'x64' : 'x86' 29 | @full_version = node['platform_version'] 30 | @nt_version = @full_version.to_f 31 | @is_core = ::MSDotNet::WindowsVersionHelper.core_version?(node) 32 | @is_server = ::MSDotNet::WindowsVersionHelper.server_version?(node) 33 | 34 | @machine_type = if core? 35 | :core 36 | elsif server? 37 | :server 38 | else 39 | :workstation 40 | end 41 | end 42 | 43 | def packages 44 | @packages ||= ::Mash.new( 45 | '3.5.SP1' => { 46 | name: 'Microsoft .NET Framework 3.5 Service Pack 1', 47 | url: 'https://download.microsoft.com/download/2/0/E/20E90413-712F-438C-988E-FDAA79A8AC3D/dotnetfx35.exe', 48 | checksum: '0582515bde321e072f8673e829e175ed2e7a53e803127c50253af76528e66bc1', 49 | }, 50 | '4.6.2' => { 51 | name: 'Microsoft .NET Framework 4.6.2', 52 | url: 'https://download.visualstudio.microsoft.com/download/pr/8e396c75-4d0d-41d3-aea8-848babc2736a/80b431456d8866ebe053eb8b81a168b3/ndp462-kb3151800-x86-x64-allos-enu.exe', 53 | checksum: 'b4cbb4bc9a3983ec3be9f80447e0d619d15256a9ce66ff414ae6e3856705e237', 54 | not_if: %w(KB3151804 KB3151864 KB3151900), 55 | }, 56 | '4.7' => { 57 | name: 'Microsoft .NET Framework 4.7', 58 | url: 'https://download.visualstudio.microsoft.com/download/pr/2dfcc711-bb60-421a-a17b-76c63f8d1907/e5c0231bd5d51fffe65f8ed7516de46a/ndp47-kb3186497-x86-x64-allos-enu.exe', 59 | checksum: 'd9690c83d7ce56b2804ea34aef79ce34b242d60b9cec16385bce1340cfe00883', 60 | not_if: %w(KB3186505 KB3186539 KB3186568), 61 | }, 62 | '4.7.1' => { 63 | name: 'Microsoft .NET Framework 4.7.1', 64 | url: 'https://download.visualstudio.microsoft.com/download/pr/4312fa21-59b0-4451-9482-a1376f7f3ba4/9947fce13c11105b48cba170494e787f/ndp471-kb4033342-x86-x64-allos-enu.exe', 65 | checksum: 'df6e700d37ff416e2e1d8463dededdf76522ceaf5bb4cc3f197a7f2b9eccc4ad', 66 | not_if: %w(KB4033342 KB4033345 KB4033369 KB4033393), 67 | }, 68 | '4.7.2' => { 69 | name: 'Microsoft .NET Framework 4.7.2', 70 | url: 'https://download.visualstudio.microsoft.com/download/pr/1f5af042-d0e4-4002-9c59-9ba66bcf15f6/089f837de42708daacaae7c04b7494db/ndp472-kb4054530-x86-x64-allos-enu.exe', 71 | checksum: '5cb624b97f9fd6d3895644c52231c9471cd88aacb57d6e198d3024a1839139f6', 72 | not_if: %w(KB4054542 KB4054566 KB4054590 KB4073120), 73 | }, 74 | '4.8' => { 75 | name: 'Microsoft .NET Framework 4.8', 76 | url: 'https://download.visualstudio.microsoft.com/download/pr/2d6bb6b2-226a-4baa-bdec-798822606ff1/8494001c276a4b96804cde7829c04d7f/ndp48-x86-x64-allos-enu.exe', 77 | checksum: '68c9986a8dcc0214d909aa1f31bee9fb5461bb839edca996a75b08ddffc1483f', 78 | not_if: %w(KB4503548 KB4486081 KB4486105 KB4486129 KB4486153), 79 | }, 80 | ########### 81 | # Patches 82 | ########### 83 | 'KB2919442' => { 84 | name: 'Update for Microsoft Windows (KB2919442)', 85 | url: if x64? 86 | 'https://download.microsoft.com/download/D/6/0/D60ED3E0-93A5-4505-8F6A-8D0A5DA16C8A/Windows8.1-KB2919442-x64.msu' 87 | else 88 | 'https://download.microsoft.com/download/9/D/A/9DA6C939-9E65-4681-BBBE-A8F73A5C116F/Windows8.1-KB2919442-x86.msu' 89 | end, 90 | options: '/norestart /quiet', 91 | checksum: x64? ? 'c10787e669b484674584a990e069295e8b81b5366f98508010a3ae181b729482' : '3368c3a329f402fd982b15b399368627b96973f008a5456b5286bdfc10c1169b', 92 | not_if: %w(KB2919442 KB3173424 KB3021910 KB3012199 KB2989647 KB2975061 KB2969339 KB2904440), 93 | }, 94 | 'KB3173424' => { 95 | name: 'Update for Microsoft Windows (KB3173424)', 96 | url: if x64? 97 | 'https://download.microsoft.com/download/E/2/C/E2CA92A3-8D60-4702-98E2-2AB396FEA1BC/Windows8.1-KB3173424-x64.msu' 98 | else 99 | 'https://download.microsoft.com/download/4/5/F/45F8AA2A-1C72-460A-B9E9-83D3966DDA46/Windows8.1-KB3173424-x86.msu' 100 | end, 101 | options: '/norestart /quiet', 102 | checksum: x64? ? '2c6c577e4e231ce6b020e5b9a2766154f474c6ecae82735ba5ec03875d64895b' : '91bf481343be03cc310c50167be8ea1af92113048c99b7b91f1b2b03628b0dcd', 103 | not_if: %w(KB3173424), 104 | }, 105 | 'KB2919355' => { 106 | name: 'Update for Microsoft Windows (KB2919355)', 107 | url: if x64? 108 | 'https://download.microsoft.com/download/2/5/6/256CCCFB-5341-4A8D-A277-8A81B21A1E35/Windows8.1-KB2919355-x64.msu' 109 | else 110 | 'https://download.microsoft.com/download/4/E/C/4EC66C83-1E15-43FD-B591-63FB7A1A5C04/Windows8.1-KB2919355-x86.msu' 111 | end, 112 | options: '/norestart /quiet', 113 | checksum: x64? ? 'b0c9ada530f5ee90bb962afa9ed26218c582362315e13b1ba97e59767cb7825d' : 'f8beca5b463a36e1fef45ad0dca6a0de7606930380514ac1852df5ca6e3f6c1d', 114 | not_if: %w(KB2919355), 115 | }, 116 | 'KB4019990-6.2' => { 117 | name: 'Update for Microsoft Windows (KB4019990)', 118 | url: 'https://download.microsoft.com/download/2/F/4/2F4F48F4-D980-43AA-906A-8FFF40BCB832/Windows8-RT-KB4019990-x64.msu', 119 | options: '/norestart /quiet', 120 | checksum: 'f50efbd614094ebe84b0bccb0f89903e5619e5a380755d0e8170e8e893af7a9f', 121 | not_if: %w(KB4019990), 122 | }, 123 | 'KB4054852' => { 124 | name: 'Update for Microsoft .NET Framework 4.7.1 (KB4054852)', 125 | url: 'https://download.microsoft.com/download/8/5/E/85E5D4E2-7D95-40F7-AB83-9DBFCFBDBE6E/NDP471-KB4054856-x86-x64-AllOS.exe', 126 | options: '/norestart /quiet', 127 | checksum: 'f98d12d84c803699d149a254ada3dd5dc0698307638e1baae209cc6f3a729e29', 128 | }, 129 | 'KB4054853' => { 130 | name: 'Update for Microsoft Windows (KB4054853)', # Cosmetic name 131 | url: 'https://download.microsoft.com/download/8/5/E/85E5D4E2-7D95-40F7-AB83-9DBFCFBDBE6E/NDP471-KB4054856-x86-x64-AllOS.exe', 132 | options: '/norestart /quiet', 133 | checksum: 'f98d12d84c803699d149a254ada3dd5dc0698307638e1baae209cc6f3a729e29', 134 | not_if: %w(KB4054853), 135 | }, 136 | 'KB4054854' => { 137 | name: 'Update for Microsoft Windows (KB4054854)', # Cosmetic name 138 | url: 'https://download.microsoft.com/download/8/5/E/85E5D4E2-7D95-40F7-AB83-9DBFCFBDBE6E/NDP471-KB4054856-x86-x64-AllOS.exe', 139 | options: '/norestart /quiet', 140 | checksum: 'f98d12d84c803699d149a254ada3dd5dc0698307638e1baae209cc6f3a729e29', 141 | not_if: %w(KB4054854), 142 | }, 143 | 'KB4054855' => { 144 | name: 'Update for Microsoft Windows (KB4054855)', # Cosmetic name 145 | url: 'https://download.microsoft.com/download/8/5/E/85E5D4E2-7D95-40F7-AB83-9DBFCFBDBE6E/NDP471-KB4054856-x86-x64-AllOS.exe', 146 | options: '/norestart /quiet', 147 | checksum: 'f98d12d84c803699d149a254ada3dd5dc0698307638e1baae209cc6f3a729e29', 148 | not_if: %w(KB4054855), 149 | }, 150 | 'KB4073120' => { 151 | name: 'Update for Microsoft Windows (KB4073120)', # Cosmetic name 152 | url: 'http://download.windowsupdate.com/d/msdownload/update/software/ftpk/2018/07/windows10.0-kb4073120-x64_3ec6124b919d92627020d226bdcc34695c7c7080.msu', 153 | options: '/norestart /quiet', 154 | checksum: 'b029cfe1d1995009c856a147560b35ed858faf1f300fd42c0a0b4e0df99bfcfa', 155 | not_if: %w(KB4073120), 156 | }, 157 | ) 158 | end 159 | 160 | protected 161 | 162 | def core? 163 | is_core 164 | end 165 | 166 | def server? 167 | is_server 168 | end 169 | 170 | def x64? 171 | arch == 'x64' 172 | end 173 | end 174 | end 175 | -------------------------------------------------------------------------------- /libraries/v3_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # 4 | # Cookbook:: ms_dotnet 5 | # Library:: v3_helper 6 | # Author:: Baptiste Courtois () 7 | # 8 | # Copyright:: (C) 2015-2016, Criteo. 9 | # 10 | # Licensed under the Apache License, Version 2.0 (the "License"); 11 | # you may not use this file except in compliance with the License. 12 | # You may obtain a copy of the License at 13 | # 14 | # http://www.apache.org/licenses/LICENSE-2.0 15 | # 16 | # Unless required by applicable law or agreed to in writing, software 17 | # distributed under the License is distributed on an "AS IS" BASIS, 18 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 19 | # See the License for the specific language governing permissions and 20 | # limitations under the License. 21 | # 22 | require_relative 'version_helper' 23 | 24 | module MSDotNet 25 | # Provides information about .NET 3 setup 26 | # TODO: remove after 2029-01-09 27 | class V3Helper < VersionHelper 28 | def installed_version 29 | # Order is important because we want the maximum version 30 | %w(3.5).map do |version| 31 | registry_key = "HKLM\\Software\\Microsoft\\Net Framework Setup\\NDP\\v#{version}" 32 | next unless registry_key_exists? registry_key 33 | 34 | values = ::Mash[registry_get_values(registry_key).map { |e| [e[:name], e[:data]] }] 35 | next if values[:Install].to_i != 1 36 | 37 | values[:SP].to_i == '0' ? version : "#{version}.SP#{values[:SP]}" 38 | end.compact.first 39 | end 40 | 41 | def supported_versions 42 | @supported_versions ||= %w(3.5.SP1) 43 | end 44 | 45 | protected 46 | 47 | def feature_names 48 | @feature_names ||= nt_version >= 6.0 ? %w(NetFx3) : [] 49 | end 50 | 51 | def feature_setup 52 | @feature_setup ||= case nt_version 53 | # Vista & Server 2008 54 | when 6.0 then %w(3.0) 55 | # 7, 8, 8.1, 10 & Server 2008R2, 2012, 2012R2 56 | when 6.1, 6.2, 6.3, 10 then %w(3.0 3.5 3.5.SP1) 57 | # Other versions 58 | else [] 59 | end 60 | end 61 | 62 | def patch_names 63 | @patch_names ||= {} 64 | end 65 | 66 | def package_setup 67 | @package_setup ||= case nt_version 68 | # Windows XP & Server 2003 69 | when 5.2, 5.3 then %w(3.0 3.5 3.5.SP1) 70 | # Vista & Server 2008 71 | when 6.0 then %w(3.5 3.5.SP1) 72 | # Other versions 73 | else [] 74 | end 75 | end 76 | 77 | def prerequisite_names 78 | @prerequisite_names ||= {} 79 | end 80 | end 81 | end 82 | -------------------------------------------------------------------------------- /libraries/v4_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # 4 | # Cookbook:: ms_dotnet 5 | # Library:: v4_helper 6 | # Author:: Baptiste Courtois () 7 | # 8 | # Copyright:: (C) 2015-2016, Criteo. 9 | # 10 | # Licensed under the Apache License, Version 2.0 (the "License"); 11 | # you may not use this file except in compliance with the License. 12 | # You may obtain a copy of the License at 13 | # 14 | # http://www.apache.org/licenses/LICENSE-2.0 15 | # 16 | # Unless required by applicable law or agreed to in writing, software 17 | # distributed under the License is distributed on an "AS IS" BASIS, 18 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 19 | # See the License for the specific language governing permissions and 20 | # limitations under the License. 21 | # 22 | require_relative 'version_helper' 23 | 24 | module MSDotNet 25 | # Provides information about .NET 4 setup 26 | class V4Helper < VersionHelper 27 | REGISTRY_KEY = 'HKLM\Software\Microsoft\Net Framework Setup\NDP\v4\Full' unless defined? REGISTRY_KEY 28 | 29 | def installed_version 30 | return unless registry_key_exists? REGISTRY_KEY 31 | 32 | values = ::Mash[registry_get_values(REGISTRY_KEY).map { |e| [e[:name], e[:data]] }] 33 | 34 | return if values[:Install].to_i != 1 35 | 36 | release = values[:Release].to_i 37 | if release >= 528_040 38 | '4.8' 39 | elsif release >= 461_808 40 | '4.7.2' 41 | elsif release >= 461_308 42 | '4.7.1' 43 | elsif release >= 460_798 44 | '4.7' 45 | elsif release >= 394_802 46 | '4.6.2' 47 | end 48 | end 49 | 50 | def supported_versions 51 | @supported_versions ||= %w(4.6.2 4.7 4.7.1 4.7.2 4.8) 52 | end 53 | 54 | protected 55 | 56 | def feature_names 57 | @feature_names ||= case nt_version 58 | when 6.2, 6.3, 10 59 | # TODO: check 2012R2 Core feature name 60 | core? ? %w(netFx4-Server-Core) : %w(NetFx4) 61 | else 62 | [] 63 | end 64 | end 65 | 66 | def feature_setup 67 | @feature_setup ||= [].tap do |result| 68 | version = ::Gem::Version.new(full_version) 69 | # Windows 10 & Server 2016 v1607 (RS1) & newer 70 | result << '4.6.2' if version >= ::Gem::Version.new('10.0.14393') 71 | # Windows 10 v1703 (RS2) & newer 72 | result << '4.7' if version >= ::Gem::Version.new('10.0.15063') 73 | # Windows 10 & Server 2016 v1709 (RS3) & newer 74 | result << '4.7.1' if version >= ::Gem::Version.new('10.0.16299') 75 | # Windows 10 & Server 2016 v1803 (RS4) & newer 76 | result << '4.7.2' if version >= ::Gem::Version.new('10.0.17134') 77 | # Windows 10 v1903 (19H1) & newer 78 | result << '4.8' if version >= ::Gem::Version.new('10.0.18362') 79 | end 80 | end 81 | 82 | def patch_names 83 | @patch_names ||= case full_version 84 | when /^6\.2/ # TODO: remove after 2023-10-10 85 | { '4.7.1' => %w(KB4054853) } 86 | when /^6\.3/ # TODO: remove after 2023-10-10 87 | { '4.7.1' => %w(KB4054854) } 88 | when '10.0.10240', '10.0.10586', '10.0.14393', '10.0.15063' 89 | { '4.7.1' => %w(KB4054855) } 90 | when '10.0.16299' 91 | { '4.7.2' => %w(KB4073120) } 92 | else 93 | {} 94 | end 95 | end 96 | 97 | def package_setup 98 | @package_setup ||= [].tap do |result| 99 | version = ::Gem::Version.new(full_version) 100 | # Up to Windows 10 v1903, Server 2022 101 | result << '4.8' if version < ::Gem::Version.new('10.0.18362') 102 | # Up to Windows 10 v1803, Server 2019 103 | result << '4.7.2' if version < ::Gem::Version.new('10.0.17763') 104 | # Up to Windows 10 & Server 2016 v1709 (RS3) 105 | result << '4.7.1' if version < ::Gem::Version.new('10.0.16299') 106 | # Up to Windows 10 v1703 (RS2) 107 | result << '4.7' if version < ::Gem::Version.new('10.0.15063') 108 | # Up to Windows 10 & Server 2016 v1607 (RS1) 109 | result << '4.6.2' if version < ::Gem::Version.new('10.0.14393') 110 | end 111 | end 112 | 113 | def prerequisite_names 114 | @prerequisite_names ||= case nt_version 115 | when 6.2 # TODO: remove after 2023-10-10 116 | { '4.7' => ['KB4019990-6.2'] } 117 | when 6.3 # TODO: remove after 2023-10-10 118 | prerequisites46 = %w(KB2919442 KB2919355 KB3173424) 119 | { 120 | '4.6.2' => prerequisites46, 121 | } 122 | else 123 | {} 124 | end 125 | end 126 | end 127 | end 128 | -------------------------------------------------------------------------------- /libraries/version_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # 4 | # Cookbook:: ms_dotnet 5 | # Library:: version_helper 6 | # Author:: Baptiste Courtois () 7 | # 8 | # Copyright:: (C) 2015-2016, Criteo. 9 | # 10 | # Licensed under the Apache License, Version 2.0 (the "License"); 11 | # you may not use this file except in compliance with the License. 12 | # You may obtain a copy of the License at 13 | # 14 | # http://www.apache.org/licenses/LICENSE-2.0 15 | # 16 | # Unless required by applicable law or agreed to in writing, software 17 | # distributed under the License is distributed on an "AS IS" BASIS, 18 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 19 | # See the License for the specific language governing permissions and 20 | # limitations under the License. 21 | # 22 | require_relative 'package_helper' 23 | 24 | module MSDotNet 25 | # Base "abstract" class for .NET version helper 26 | # Provides method to easily determine how to setup a .NET version 27 | class VersionHelper < PackageHelper 28 | # Used to detect installed version 29 | include ::Chef::DSL::RegistryHelper 30 | 31 | attr_reader :run_context 32 | 33 | def initialize(node) 34 | raise TypeError, 'MSDotNet::VersionHelper is an "abstract" class and must not be instanciated directly.' if self.class == ::MSDotNet::VersionHelper 35 | 36 | super 37 | 38 | # run_context is required by ::Chef::DSL::RegistryHelper 39 | @run_context = node.run_context 40 | end 41 | 42 | # Get windows features required by the given .NET version 43 | def features(version) 44 | feature_setup.include?(version) ? feature_names : [] 45 | end 46 | 47 | # Get installed .NET version on the current node 48 | # Returns a String or nil 49 | def installed_version 50 | raise NotImplementedError 51 | end 52 | 53 | # Get windows package required by the given .NET version 54 | def package(version) 55 | packages[version] if package_setup.include? version 56 | end 57 | 58 | # Get windows patches required by the given .NET version 59 | def patches(version) 60 | (patch_names[version] || []).map { |patch| packages[patch] } 61 | end 62 | 63 | # Get windows packages required prior to install the given .NET version 64 | def prerequisites(version) 65 | (prerequisite_names[version] || []).map { |package| packages[package] } 66 | end 67 | 68 | # Get all .NET versions supported on the current node OS 69 | # Returns an Array 70 | def supported_versions 71 | raise NotImplementedError 72 | end 73 | 74 | protected 75 | 76 | # Get windows feature's names for the major .NET version on the current node OS 77 | # Returns an Array 78 | def feature_names 79 | raise NotImplementedError 80 | end 81 | 82 | # Get all .NET versions requiring windows feature activation on the current node OS 83 | # Returns an Array 84 | def feature_setup 85 | raise NotImplementedError 86 | end 87 | 88 | # Get patch package's names for each minor .NET versions on the current node OS 89 | # Returns a Hash> with .NET version as key, and package names as value 90 | def patch_names 91 | raise NotImplementedError 92 | end 93 | 94 | # Get all .NET versions requiring windows package install on the current node OS 95 | # Returns an Array 96 | def package_setup 97 | raise NotImplementedError 98 | end 99 | 100 | # Get prerequisite package's names for each minor .NET versions on the current node OS 101 | # Returns a Hash> with .NET version as key, and package names as value 102 | def prerequisites_names 103 | raise NotImplementedError 104 | end 105 | end 106 | end 107 | -------------------------------------------------------------------------------- /libraries/windows_version_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # 4 | # Cookbook:: ms_dotnet 5 | # Library:: windows_version_helper 6 | # Author:: Baptiste Courtois () 7 | # 8 | # Copyright:: 2015-2021, Criteo 9 | # 10 | # Licensed under the Apache License, Version 2.0 (the "License"); 11 | # you may not use this file except in compliance with the License. 12 | # You may obtain a copy of the License at 13 | # 14 | # http://www.apache.org/licenses/LICENSE-2.0 15 | # 16 | # Unless required by applicable law or agreed to in writing, software 17 | # distributed under the License is distributed on an "AS IS" BASIS, 18 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 19 | # See the License for the specific language governing permissions and 20 | # limitations under the License. 21 | # 22 | module MSDotNet 23 | # Module based on windows ohai kernel.cs_info providing version helpers 24 | module WindowsVersionHelper 25 | # Module referencing CORE SKU contants from product type 26 | # see. https://msdn.microsoft.com/windows/desktop/ms724358#PRODUCT_DATACENTER_SERVER_CORE 27 | # n.b. Prefix - PRODUCT_ - and suffix - _CORE- have been removed 28 | module CoreSKU 29 | # Server Datacenter Core 30 | DATACENTER_SERVER = 0x0C unless constants.include?(:DATACENTER_SERVER) 31 | # Server Datacenter without Hyper-V Core 32 | DATACENTER_SERVER_V = 0x27 unless constants.include?(:DATACENTER_SERVER_V) 33 | # Server Enterprise Core 34 | ENTERPRISE_SERVER = 0x0E unless constants.include?(:ENTERPRISE_SERVER) 35 | # Server Enterprise without Hyper-V Core 36 | ENTERPRISE_SERVER_V = 0x29 unless constants.include?(:ENTERPRISE_SERVER_V) 37 | # Server Standard Core 38 | STANDARD_SERVER = 0x0D unless constants.include?(:STANDARD_SERVER) 39 | # Server Standard without Hyper-V Core 40 | STANDARD_SERVER_V = 0x28 unless constants.include?(:STANDARD_SERVER_V) 41 | # Small Business Server Premium Core 42 | PRODUCT_SMALLBUSINESS_SERVER_PREMIUM_CORE = 0x3F unless constants.include?(:PRODUCT_SMALLBUSINESS_SERVER_PREMIUM_CORE) 43 | # Server Solutions Premium Core 44 | STANDARD_SERVER_SOLUTIONS = 0x35 unless constants.include?(:STANDARD_SERVER_SOLUTIONS) 45 | # Storage Server Enterprise Core 46 | STORAGE_ENTERPRISE_SERVER = 0x2E unless constants.include?(:STORAGE_ENTERPRISE_SERVER) 47 | # Storage Server Express Core 48 | STORAGE_EXPRESS_SERVER = 0x2B unless constants.include?(:STORAGE_EXPRESS_SERVER) 49 | # Storage Server Standard Core 50 | STORAGE_STANDARD_SERVER = 0x2C unless constants.include?(:STORAGE_STANDARD_SERVER) 51 | # Storage Server Workgroup Core 52 | STORAGE_WORKGROUP_SERVER = 0x2D unless constants.include?(:STORAGE_WORKGROUP_SERVER) 53 | # Web Server Core 54 | WEB_SERVER = 0x1D unless constants.include?(:WEB_SERVER) 55 | end 56 | 57 | # Module referencing product type contants 58 | # see. https://msdn.microsoft.com/windows/desktop/ms724833#VER_NT_SERVER 59 | # n.b. Prefix - VER_NT_ - has been removed 60 | module ProductType 61 | WORKSTATION = 0x1 unless constants.include?(:WORKSTATION) 62 | DOMAIN_CONTROLLER = 0x2 unless constants.include?(:DOMAIN_CONTROLLER) 63 | SERVER = 0x3 unless constants.include?(:SERVER) 64 | end 65 | 66 | # Determines whether current node is running a windows Core version 67 | def self.core_version?(node) 68 | validate_platform node 69 | 70 | CoreSKU.constants.any? { |c| CoreSKU.const_get(c) == node['kernel']['os_info']['operating_system_sku'] } 71 | end 72 | 73 | # Determines whether current node is a workstation version 74 | def self.workstation_version?(node) 75 | validate_platform node 76 | node['kernel']['os_info']['product_type'] == ProductType::WORKSTATION 77 | end 78 | 79 | # Determines whether current node is a server version 80 | def self.server_version?(node) 81 | !workstation_version?(node) 82 | end 83 | 84 | # Determines NT version of the current node 85 | def self.nt_version(node) 86 | validate_platform node 87 | 88 | node['platform_version'].to_f 89 | end 90 | 91 | def self.validate_platform(node) 92 | raise 'Windows helper are only supported on windows platform!' unless node['platform'] == 'windows' 93 | end 94 | end 95 | end 96 | -------------------------------------------------------------------------------- /metadata.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | name 'ms_dotnet' 4 | maintainer 'Criteo' 5 | maintainer_email 'b.courtois@criteo.com' 6 | license 'Apache-2.0' 7 | description 'Installs/Configures ms_dotnet' 8 | version '6.0.0' 9 | supports 'windows' 10 | 11 | chef_version '>= 14.7' 12 | 13 | source_url 'https://github.com/criteo-cookbooks/ms_dotnet' 14 | issues_url 'https://github.com/criteo-cookbooks/ms_dotnet/issues' 15 | -------------------------------------------------------------------------------- /recipes/default.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | # 3 | # Cookbook Name:: ms_dotnet 4 | # Recipe:: default 5 | # Author:: Jeremy Mauro () 6 | # 7 | # Copyright (C) 2014 Chef Software, Inc. 8 | # 9 | # Licensed under the Apache License, Version 2.0 (the "License"); 10 | # you may not use this file except in compliance with the License. 11 | # You may obtain a copy of the License at 12 | # 13 | # http://www.apache.org/licenses/LICENSE-2.0 14 | # 15 | # Unless required by applicable law or agreed to in writing, software 16 | # distributed under the License is distributed on an "AS IS" BASIS, 17 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | # See the License for the specific language governing permissions and 19 | # limitations under the License. 20 | # 21 | -------------------------------------------------------------------------------- /recipes/ms_dotnet3.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # 4 | # Cookbook:: ms_dotnet 5 | # Recipe:: ms_dotnet3 6 | # Author:: Baptiste Courtois () 7 | # 8 | # Copyright:: (C) 2015-2016, Criteo. 9 | # 10 | # Licensed under the Apache License, Version 2.0 (the "License"); 11 | # you may not use this file except in compliance with the License. 12 | # You may obtain a copy of the License at 13 | # 14 | # http://www.apache.org/licenses/LICENSE-2.0 15 | # 16 | # Unless required by applicable law or agreed to in writing, software 17 | # distributed under the License is distributed on an "AS IS" BASIS, 18 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 19 | # See the License for the specific language governing permissions and 20 | # limitations under the License. 21 | # 22 | return unless platform? 'windows' 23 | 24 | v3_info = node['ms_dotnet']['v3'] 25 | 26 | ms_dotnet_framework v3_info['version'] do 27 | timeout node['ms_dotnet']['timeout'] 28 | include_patches v3_info['include_patches'] 29 | feature_source v3_info['feature_source'] unless v3_info['feature_source'].nil? 30 | perform_reboot v3_info['perform_reboot'] 31 | package_sources v3_info['package_sources'] 32 | require_support v3_info['require_support'] 33 | end 34 | -------------------------------------------------------------------------------- /recipes/ms_dotnet4.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # 4 | # Cookbook:: ms_dotnet 5 | # Recipe:: ms_dotnet4 6 | # Author:: Baptiste Courtois () 7 | # 8 | # Copyright:: (C) 2015-2016, Criteo. 9 | # 10 | # Licensed under the Apache License, Version 2.0 (the "License"); 11 | # you may not use this file except in compliance with the License. 12 | # You may obtain a copy of the License at 13 | # 14 | # http://www.apache.org/licenses/LICENSE-2.0 15 | # 16 | # Unless required by applicable law or agreed to in writing, software 17 | # distributed under the License is distributed on an "AS IS" BASIS, 18 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 19 | # See the License for the specific language governing permissions and 20 | # limitations under the License. 21 | # 22 | return unless platform? 'windows' 23 | 24 | v4_info = node['ms_dotnet']['v4'] 25 | 26 | ms_dotnet_framework v4_info['version'] do 27 | timeout node['ms_dotnet']['timeout'] 28 | include_patches v4_info['include_patches'] 29 | feature_source v4_info['feature_source'] unless v4_info['feature_source'].nil? 30 | perform_reboot v4_info['perform_reboot'] 31 | package_sources v4_info['package_sources'] 32 | require_support v4_info['require_support'] 33 | end 34 | -------------------------------------------------------------------------------- /recipes/regiis.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # 4 | # Cookbook:: ms_dotnet 5 | # Recipe:: regiis 6 | # Author: Kendrick Martin() 7 | # 8 | # This recipe registers .NET 4 with IIS to install ISAPI filters 9 | # 10 | # Copyright:: 2012, Webtrends, Inc. 11 | # 12 | # Licensed under the Apache License, Version 2.0 (the "License"); 13 | # you may not use this file except in compliance with the License. 14 | # You may obtain a copy of the License at 15 | # 16 | # http://www.apache.org/licenses/LICENSE-2.0 17 | # 18 | # Unless required by applicable law or agreed to in writing, software 19 | # distributed under the License is distributed on an "AS IS" BASIS, 20 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 21 | # See the License for the specific language governing permissions and 22 | # limitations under the License. 23 | # 24 | return unless platform? 'windows' 25 | 26 | guard_file = ::File.join(::Chef::Config['file_cache_path'], 'aspnet_regiis') 27 | # register once and only if IIS is installed 28 | execute 'aspnet_regiis' do 29 | action :run 30 | command "%WINDIR%\\Microsoft.Net\\Framework64\\v4.0.30319\\aspnet_regiis.exe -i -enable > #{guard_file}" 31 | creates guard_file 32 | only_if 'sc.exe query W3SVC' 33 | end 34 | -------------------------------------------------------------------------------- /resources/framework.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # 4 | # Cookbook:: ms_dotnet 5 | # Resource:: framework 6 | # Author:: Baptiste Courtois () 7 | # 8 | # Copyright:: (C) 2015-2016, Criteo. 9 | # 10 | # Licensed under the Apache License, Version 2.0 (the "License"); 11 | # you may not use this file except in compliance with the License. 12 | # You may obtain a copy of the License at 13 | # 14 | # http://www.apache.org/licenses/LICENSE-2.0 15 | # 16 | # Unless required by applicable law or agreed to in writing, software 17 | # distributed under the License is distributed on an "AS IS" BASIS, 18 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 19 | # See the License for the specific language governing permissions and 20 | # limitations under the License. 21 | # 22 | 23 | provides :ms_dotnet_framework, os: 'windows' 24 | unified_mode true if respond_to?(:unified_mode) 25 | 26 | property :feature_source, String 27 | property :include_patches, [true, false], default: true 28 | property :package_sources, Hash, default: lazy { ::Mash.new } 29 | property :perform_reboot, [true, false], default: false 30 | property :require_support, [true, false], default: true 31 | property :timeout, Integer, default: 600 32 | property :version, String, name_property: true 33 | 34 | load_current_value do |desired| 35 | version_helper = ::MSDotNet.version_helper node, desired.version.to_i 36 | 37 | version_helper.installed_version.tap do |installed_version| 38 | # Indicate the value does not exist when there is no installed version 39 | current_value_does_not_exist! if installed_version.nil? 40 | # Otherwise set it to current_value 41 | version installed_version 42 | end 43 | end 44 | 45 | action :install do 46 | if unsupported? 47 | ::Chef::Log.info "Unsupported .NET version: #{new_resource.version}" 48 | raise "Can't install unsupported .NET version `#{new_resource.version}'" if new_resource.require_support 49 | elsif superseded? 50 | ::Chef::Log.info ".NET `#{new_resource.version}' has been superseded by .NET `#{current_resource.version}'. Nothing to do!" 51 | else 52 | # Handle features 53 | install_features 54 | 55 | # Handle packages (prerequisites + main setup + patches) 56 | install_packages 57 | end 58 | end 59 | 60 | action_class do 61 | def reboot_resource 62 | @reboot_resource ||= ms_dotnet_reboot "Reboot for ms_dotnet[#{new_resource.name}]" do 63 | action :nothing 64 | end 65 | end 66 | 67 | def install_features 68 | features.each do |feature| 69 | windows_feature feature do 70 | action :install 71 | all version_helper.nt_version >= 6.2 72 | source new_resource.feature_source unless new_resource.feature_source.nil? 73 | # Perform automatic reboot now, if required 74 | notifies :reboot_if_pending, reboot_resource, :immediately if new_resource.perform_reboot 75 | end 76 | end 77 | end 78 | 79 | def install_packages 80 | [*prerequisites, package, *patches].compact.each do |pkg| 81 | windows_package pkg[:name] do 82 | action :install 83 | installer_type :custom 84 | returns [0, 3010] 85 | options pkg[:options] || '/q /norestart' 86 | timeout new_resource.timeout 87 | # Package specific info 88 | checksum pkg[:checksum] 89 | source new_resource.package_sources[pkg[:checksum]] || pkg[:url] 90 | # Handle not_if guards 91 | case pkg[:not_if] 92 | when String # Execute the given string guard 93 | not_if pkg[:not_if] 94 | when Array # Ensure given array of QFE KB is not installed 95 | # Some packages are installed as QFE updates on 2012, 2012R2 & 10 or may have been superseded by other updates 96 | not_if do 97 | require 'wmi-lite' 98 | filter = pkg[:not_if].map { |kb| " HotFixID='#{kb}'" }.join(' OR') 99 | !filter.empty? && ::WmiLite::Wmi.new.query("SELECT HotFixID FROM Win32_QuickFixEngineering WHERE #{filter}").any? 100 | end 101 | end 102 | # Perform automatic reboot now, if required 103 | notifies :reboot_if_pending, reboot_resource, :immediately if new_resource.perform_reboot 104 | end 105 | end 106 | end 107 | 108 | def version_helper 109 | @version_helper ||= ::MSDotNet.version_helper node, new_resource.version.to_i 110 | end 111 | 112 | def unsupported? 113 | !version_helper.supported_versions.include?(new_resource.version) 114 | end 115 | 116 | # This method determines whether a more recent minor version of .NET is present 117 | # In this case, there is no need to do anything. 118 | def superseded? 119 | !current_resource.nil? && ::Gem::Version.new(new_resource.version) < ::Gem::Version.new(current_resource.version) 120 | end 121 | 122 | def package 123 | version_helper.package new_resource.version 124 | end 125 | 126 | def prerequisites 127 | version_helper.prerequisites new_resource.version 128 | end 129 | 130 | def features 131 | version_helper.features new_resource.version 132 | end 133 | 134 | def patches 135 | new_resource.include_patches ? version_helper.patches(new_resource.version) : [] 136 | end 137 | end 138 | -------------------------------------------------------------------------------- /resources/reboot.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # 4 | # Cookbook:: ms_dotnet 5 | # Resource:: reboot 6 | # Author:: Baptiste Courtois () 7 | # 8 | # Copyright:: (C) 2017, Criteo. 9 | # 10 | # Licensed under the Apache License, Version 2.0 (the "License"); 11 | # you may not use this file except in compliance with the License. 12 | # You may obtain a copy of the License at 13 | # 14 | # http://www.apache.org/licenses/LICENSE-2.0 15 | # 16 | # Unless required by applicable law or agreed to in writing, software 17 | # distributed under the License is distributed on an "AS IS" BASIS, 18 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 19 | # See the License for the specific language governing permissions and 20 | # limitations under the License. 21 | # 22 | 23 | provides :ms_dotnet_reboot, os: 'windows' 24 | unified_mode true if respond_to?(:unified_mode) 25 | 26 | property :source, [String, Resource], desired_state: false 27 | 28 | action :reboot_if_pending do 29 | source_name = new_resource.source || 'ms_dotnet_framework' 30 | reboot "Reboot for #{source_name}" do 31 | action :reboot_now 32 | delay_mins node['ms_dotnet']['reboot']['delay'] 33 | reason "Reboot by chef for #{source_name}" 34 | only_if { reboot_pending? } 35 | end 36 | end 37 | 38 | # Override run_action to store the notifying_resource! 39 | def run_action(action, notification_type = nil, notifying_resource = nil) 40 | source(notifying_resource) if source.nil? && !notifying_resource.nil? 41 | super 42 | end 43 | 44 | action_class do 45 | end 46 | -------------------------------------------------------------------------------- /spec/data/2019_registry.yml: -------------------------------------------------------------------------------- 1 | HKLM: 2 | Software: 3 | Microsoft: 4 | Net Framework Setup: 5 | NDP: 6 | v4: 7 | Full: 8 | Release: 461814 9 | Install: 1 10 | -------------------------------------------------------------------------------- /spec/libraries/default_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe ::MSDotNet do 6 | let(:node) { init_node fauxhai_data } 7 | 8 | describe 'version_helper' do 9 | it 'returns a new instance of MSDotNet::VersionHelper\'s subclass' do 10 | SUPPORTED_MAJOR_VERSIONS.each do |major_version| 11 | helper1 = ::MSDotNet.version_helper node, major_version 12 | helper2 = ::MSDotNet.version_helper node, major_version 13 | 14 | expect(helper1).to_not eql helper2 15 | expect(helper1.is_a?(::MSDotNet::VersionHelper)).to be true 16 | expect(helper2.is_a?(::MSDotNet::VersionHelper)).to be true 17 | end 18 | end 19 | 20 | it 'fails with unsupported version' do 21 | expect { ::MSDotNet.version_helper node, -42 }.to raise_error(ArgumentError, /unsupported/i) 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /spec/libraries/package_helper_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | require_relative '../../libraries/windows_version_helper' 5 | 6 | shared_examples 'package_helper' do |data, conf| 7 | describe ::MSDotNet::PackageHelper do 8 | let(:package_helper) do 9 | # Set Core SKU 10 | data['kernel']['os_info']['operating_system_sku'] = conf[:core?] ? 0x0D : 0x00 11 | # Set arch 12 | data['kernel']['machine'] = conf[:x64?] ? 'x86_64' : 'x86' 13 | ::MSDotNet::PackageHelper.new init_node(data) 14 | end 15 | 16 | describe 'packages' do 17 | it 'returns a Mash' do 18 | expect(package_helper.packages).to be_a(Mash) 19 | end 20 | it 'returns always the same Mash' do 21 | expect(package_helper.packages).to be package_helper.packages 22 | end 23 | end 24 | 25 | %i(x64? core? server?).each do |function| 26 | describe function do 27 | it "returns #{conf[function]}" do 28 | expect(package_helper.send(function)).to be conf[function] 29 | end 30 | end 31 | end 32 | end 33 | end 34 | 35 | FAUXHAI_WINDOWS_VERSIONS.each do |windows_version, version_support| 36 | # load the data 37 | data = fauxhai_data 'windows', windows_version 38 | is_server = version_support[:server] 39 | 40 | version_support[:arch].each do |arch| 41 | is_arch64 = arch == '64' 42 | 43 | describe "On Windows#{windows_version}-#{arch}" do 44 | include_examples 'package_helper', data, x64?: is_arch64, server?: is_server, core?: false 45 | end 46 | 47 | next unless version_support[:core] 48 | 49 | describe "On Windows#{windows_version}-#{arch}-CORE" do 50 | include_examples 'package_helper', data, x64?: is_arch64, server?: is_server, core?: true 51 | end 52 | end 53 | end 54 | -------------------------------------------------------------------------------- /spec/libraries/version_helper_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe ::MSDotNet::VersionHelper do 6 | class TestVersionHelper < ::MSDotNet::VersionHelper; end 7 | let(:node) { init_node fauxhai_data } 8 | subject(:test_helper) { TestVersionHelper.new node } 9 | 10 | describe 'initialize' do 11 | it 'fails when called not inherited' do 12 | expect { ::MSDotNet::VersionHelper.new node }.to raise_error(TypeError, /abstract/i) 13 | end 14 | end 15 | 16 | %w( 17 | installed_version 18 | supported_versions 19 | feature_names 20 | feature_setup 21 | patch_names 22 | package_setup 23 | ).each do |abstract_method| 24 | describe abstract_method do 25 | it 'fails with NotImplementedError' do 26 | expect { test_helper.send abstract_method.to_sym }.to raise_error NotImplementedError 27 | end 28 | end 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /spec/libraries/vx_helper_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | UNSUPPORTED_VERSIONS = %w(0.42.unsupported).freeze 6 | FAUXHAI_WINDOWS_VERSIONS.each_key do |windows_version| 7 | describe "On Windows#{windows_version}" do 8 | let(:node) { init_node fauxhai_data('windows', windows_version) } 9 | 10 | SUPPORTED_MAJOR_VERSIONS.each do |major_version| 11 | describe ::MSDotNet.const_get("V#{major_version}Helper") do 12 | # Small lambda to check if a hash looks like a package 13 | let(:package_like) { ->(hash) { %w(name url checksum).all? { |k| hash.key? k } } } 14 | let(:version_helper) { ::MSDotNet.version_helper node, major_version } 15 | 16 | describe 'installed_version' do 17 | it 'checks the registry to detect the latest version installed' do 18 | expect(version_helper).to receive(:registry_key_exists?).at_least :once 19 | expect { version_helper.installed_version }.to_not raise_error 20 | end 21 | end 22 | 23 | describe 'features' do 24 | it 'returns a - possibly empty - array of feature names' do 25 | (version_helper.supported_versions + UNSUPPORTED_VERSIONS).each do |version| 26 | expect(version_helper.features(version)).to be_an(Array).and all be_a(String) 27 | end 28 | end 29 | end 30 | 31 | describe 'package' do 32 | it 'returns a hash with package info or nil' do 33 | (version_helper.supported_versions + UNSUPPORTED_VERSIONS).each do |version| 34 | expect(version_helper.package(version)).to be_nil.or satisfy(&package_like) 35 | end 36 | end 37 | end 38 | 39 | describe 'patches' do 40 | it 'returns a - possibly empty - array of patch packages' do 41 | (version_helper.supported_versions + UNSUPPORTED_VERSIONS).each do |version| 42 | expect(version_helper.patches(version)).to be_an(Array).and all satisfy(&package_like) 43 | end 44 | end 45 | end 46 | 47 | describe 'supported_versions' do 48 | it 'returns a non-empty String array parsable by Gem::Version' do 49 | result = version_helper.supported_versions 50 | expect(result).to be_an Array 51 | expect(result).to_not be_empty 52 | expect(result).to all(be_a(String).and(satisfy { |version| ::Gem::Version.new version })) 53 | end 54 | end 55 | end 56 | end 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /spec/recipes/default_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe 'ms_dotnet::default' do 6 | describe 'On windows platform' do 7 | let(:windows_chef_run) do 8 | ChefSpec::SoloRunner.new(platform: 'windows', version: '2012R2').converge(described_recipe) 9 | end 10 | 11 | it 'does nothing' do 12 | expect(windows_chef_run.resource_collection).to be_empty 13 | end 14 | end 15 | describe 'On non-windows platform' do 16 | let(:chef_run) do 17 | ChefSpec::SoloRunner.new(platform: 'centos', version: '7').converge(described_recipe) 18 | end 19 | 20 | it 'does nothing' do 21 | expect(chef_run.resource_collection).to be_empty 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /spec/recipes/ms_dotnet3_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe 'ms_dotnet::ms_dotnet3' do 6 | describe 'On windows platform' do 7 | let(:chef_run) do 8 | ChefSpec::SoloRunner.new(platform: 'windows', version: '2012R2').converge(described_recipe) 9 | end 10 | 11 | it 'installs .NET framework v3' do 12 | expect(chef_run).to install_ms_dotnet_framework('3.5.SP1').with(DEFAULT_FRAMEWORK_ATTRIBUTES) 13 | end 14 | end 15 | 16 | describe 'On non-windows platform' do 17 | let(:chef_run) do 18 | ChefSpec::SoloRunner.new(platform: 'centos', version: '7').converge(described_recipe) 19 | end 20 | 21 | it 'does nothing' do 22 | expect(chef_run.resource_collection).to be_empty 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /spec/recipes/ms_dotnet4_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe 'ms_dotnet::ms_dotnet4' do 6 | describe 'On windows platform' do 7 | let(:chef_run) do 8 | ChefSpec::SoloRunner.new(platform: 'windows', version: '2012R2').converge(described_recipe) 9 | end 10 | 11 | it 'installs .NET framework v4' do 12 | expect(chef_run).to install_ms_dotnet_framework('4.6.2').with(DEFAULT_FRAMEWORK_ATTRIBUTES) 13 | end 14 | end 15 | 16 | describe 'On non-windows platform' do 17 | let(:chef_run) do 18 | ChefSpec::SoloRunner.new(platform: 'centos', version: '7').converge(described_recipe) 19 | end 20 | 21 | it 'does nothing' do 22 | expect(chef_run.resource_collection).to be_empty 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /spec/recipes/regiis_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe 'ms_dotnet::regiis' do 6 | describe 'On windows platform' do 7 | let(:windows_chef_run) do 8 | ChefSpec::SoloRunner.new(platform: 'windows', version: '2012R2').converge(described_recipe) 9 | end 10 | 11 | before do 12 | stub_command('sc.exe query W3SVC').and_return 1 13 | end 14 | 15 | it 'registers ASP.NET to IIS' do 16 | expect(windows_chef_run).to run_execute('aspnet_regiis') 17 | end 18 | end 19 | describe 'On non-windows platform' do 20 | let(:chef_run) do 21 | ChefSpec::SoloRunner.new(platform: 'centos', version: '7').converge(described_recipe) 22 | end 23 | 24 | it 'does nothing' do 25 | expect(chef_run.resource_collection).to be_empty 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /spec/registry_mock.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class RegistryMock 4 | def initialize(hash) 5 | @data = hash 6 | end 7 | 8 | def fetch(key_path) 9 | key_path.split(%r{\\|\/}).inject(@data) do |root, key| 10 | break unless root.is_a? Hash 11 | 12 | root[key.to_s] 13 | end 14 | end 15 | 16 | def key_exists?(key_path) 17 | fetch(key_path).is_a? Hash 18 | end 19 | 20 | def get_values(key_path) 21 | fetch(key_path).reject { |_, v| v.is_a? Hash }.map { |k, v| { name: k, data: v } } 22 | end 23 | 24 | def has_subkeys?(key_path) 25 | fetch(key_path).values.any? { |v| v.is_a? Hash } 26 | end 27 | 28 | def get_subkeys(key_path) 29 | fetch(key_path).select { |_, v| v.is_a? Hash }.keys 30 | end 31 | 32 | def value_exists?(key_path, value) 33 | fetch(key_path).key? value 34 | end 35 | 36 | def data_exists?(key_path, value) 37 | fetch(key_path)[value[:name]] == value[:data] 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /spec/resources/framework_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe 'ms_dotnet_framework' do 6 | describe 'action install' do 7 | def run_chef(platform = 'windows', version = '2019', framework_info = { version: '4.7.2' }) 8 | ChefSpec::SoloRunner.new(platform: platform, version: version, log_level: :fatal, step_into: ['ms_dotnet_framework']) do |node| 9 | framework_info.each do |key, value| 10 | node.default['ms_dotnet_test']['framework_install'][key] = value 11 | end 12 | end.converge 'ms_dotnet-test::framework_install' 13 | end 14 | 15 | context 'on non Windows platform' do 16 | it 'fails' do 17 | expect { run_chef 'centos', '7.8.2003' }.to raise_error(/Cannot find a resource for ms_dotnet_framework/) 18 | end 19 | end 20 | 21 | context 'on Windows' do 22 | before do 23 | mock_registry '2019' 24 | stub_const('WmiLite::Wmi', Class.new) 25 | allow(::WmiLite::Wmi).to receive_message_chain(:new, :query).and_return double('WmiQuery', any?: false) 26 | end 27 | 28 | it 'tries to install a .NET framework' do 29 | expect(run_chef).to install_ms_dotnet_framework('install') 30 | end 31 | 32 | context 'when newer minor version is installed' do 33 | it 'does not update the resource' do 34 | chef_run = run_chef('windows', '2019', version: '4.6.2') 35 | expect(chef_run).to_not install_windows_feature(/.*/) 36 | expect(chef_run).to_not install_windows_package(/.*/) 37 | expect(chef_run.ms_dotnet_framework('install').updated_by_last_action?).to be false 38 | end 39 | 40 | it 'logs an information message' do 41 | allow(::Chef::Log).to receive(:info) 42 | expect(::Chef::Log).to receive(:info).with '.NET `4.6.2\' has been superseded by .NET `4.7.2\'. Nothing to do!' 43 | run_chef('windows', '2019', version: '4.6.2') 44 | end 45 | end 46 | 47 | context 'when no version or an older version is installed' do 48 | it 'does install packages or features' do 49 | chef_run = run_chef('windows', '2019', version: '4.8') 50 | # ChefSpec matchers doesn't allow `.or` 51 | expect(chef_run).to install_windows_package('Microsoft .NET Framework 4.8') 52 | end 53 | it 'does not reboot if not asked' do 54 | chef_run = run_chef('windows', '2019', version: '4.8', perform_reboot: false) 55 | expect(chef_run).not_to reboot_if_pending_ms_dotnet_reboot('Reboot for ms_dotnet[install]') 56 | expect(chef_run.windows_package('Microsoft .NET Framework 4.8')).not_to notify('ms_dotnet_reboot[Reboot for ms_dotnet[install]]') 57 | end 58 | it 'reboots if asked' do 59 | chef_run = run_chef('windows', '2019', version: '4.8', perform_reboot: true) 60 | expect(chef_run.windows_package('Microsoft .NET Framework 4.8')).to notify('ms_dotnet_reboot[Reboot for ms_dotnet[install]]') 61 | end 62 | end 63 | 64 | context 'when expected version is already installed' do 65 | it 'does nothing' do 66 | chef_run = run_chef('windows', '2019', version: '4.7.2') 67 | expect(chef_run.ms_dotnet_framework('install').updated_by_last_action?).to be false 68 | end 69 | end 70 | 71 | context 'when expected major version is not supported' do 72 | it 'raises an ArgumentError' do 73 | expect { run_chef('windows', '2019', version: '42') }.to raise_error(ArgumentError, /Unsupported version '42'/) 74 | end 75 | end 76 | 77 | context 'when expected minor version is not supported' do 78 | it 'logs an information message and does nothing else' do 79 | allow(::Chef::Log).to receive(:info) 80 | expect(::Chef::Log).to receive(:info).with 'Unsupported .NET version: 4.42' 81 | expect(run_chef('windows', '2019', version: '4.42', require_support: false).ms_dotnet_framework('install').updated_by_last_action?).to be false 82 | end 83 | 84 | it 'fails if support required' do 85 | expect { run_chef('windows', '2019', version: '4.42', require_support: true) }.to raise_error(/Can't install unsupported .NET version `4.42'/) 86 | end 87 | end 88 | end 89 | end 90 | end 91 | -------------------------------------------------------------------------------- /spec/resources/reboot_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe 'ms_dotnet_reboot' do 6 | describe 'action reboot_if_pending' do 7 | def run_chef(platform = 'windows', version = '2019', reboot_info = { action: 'reboot_if_pending' }) 8 | ::ChefSpec::SoloRunner.new(platform: platform, version: version, log_level: :fatal, step_into: ['ms_dotnet_reboot']) do |node| 9 | reboot_info.each do |key, value| 10 | node.default['ms_dotnet_test']['reboot'][key] = value 11 | end 12 | end.converge 'ms_dotnet-test::reboot' 13 | end 14 | 15 | context 'on non Windows platform' do 16 | it 'fails' do 17 | expect { run_chef 'centos', '7.8.2003' }.to raise_error /Cannot find a resource for ms_dotnet_reboot/ 18 | end 19 | end 20 | 21 | context 'on Windows' do 22 | it 'does nothing if no reboot is pending' do 23 | expect_any_instance_of(::Chef::DSL::RebootPending).to receive(:reboot_pending?).and_return false 24 | expect(run_chef).not_to reboot_now_reboot('Reboot for ms_dotnet_framework') 25 | end 26 | it 'triggers the reboot if reboot was pending' do 27 | expect_any_instance_of(::Chef::DSL::RebootPending).to receive(:reboot_pending?).and_return true 28 | expect(run_chef).to reboot_now_reboot('Reboot for ms_dotnet_framework') 29 | end 30 | it 'automatically computes the source when notified' do 31 | expect_any_instance_of(::Chef::DSL::RebootPending).to receive(:reboot_pending?).and_return false 32 | reboot_resource = run_chef.ms_dotnet_reboot('now') 33 | expect(reboot_resource.source).to be_nil 34 | # Trigger notification manually, because Chefspec disable them 35 | reboot_resource.run_action :reboot_if_pending, :immediately, 'notifying_resource1' 36 | 37 | expect(reboot_resource.source).to eq 'notifying_resource1' 38 | end 39 | end 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # This file was generated by the `rspec --init` command. Conventionally, all 4 | # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`. 5 | # Require this file using `require "spec_helper"` to ensure that it is only 6 | # loaded once. 7 | # 8 | require 'chefspec' 9 | require 'chefspec/policyfile' 10 | 11 | require_relative '../libraries/default' 12 | 13 | # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration 14 | RSpec.configure do |config| 15 | config.order = 'random' 16 | end 17 | 18 | def fauxhai_data(platform = 'windows', version = '2012R2') 19 | ::Fauxhai::Mocker.new(platform: platform, version: version).data 20 | end 21 | 22 | def init_node(hash = {}) 23 | ::Chef::Node.new.tap { |node| hash.each { |k, v| node.normal[k] = v } } 24 | end 25 | 26 | require 'registry_mock' 27 | def mock_registry(data_file) 28 | require 'yaml' 29 | data = YAML.load_file ::File.join(::File.dirname(__FILE__), 'data', "#{data_file}_registry.yml") 30 | allow(::Chef::Win32::Registry).to receive(:new).and_return RegistryMock.new(data) 31 | end 32 | 33 | SUPPORTED_MAJOR_VERSIONS = [3, 4].freeze 34 | 35 | FAUXHAI_WINDOWS_VERSIONS = { 36 | '8.1' => { arch: %w(x86 x64), core: false, server: false }, 37 | '10' => { arch: %w(x86 x64), core: false, server: false }, 38 | '2012' => { arch: %w(x64), core: true, server: true }, 39 | '2012R2' => { arch: %w(x64), core: true, server: true }, 40 | '2016' => { arch: %w(x64), core: false, server: true }, 41 | '2019' => { arch: %w(x64), core: false, server: true }, 42 | }.freeze 43 | 44 | DEFAULT_FRAMEWORK_ATTRIBUTES = { 45 | feature_source: nil, 46 | include_patches: true, 47 | package_sources: {}, 48 | require_support: false, 49 | timeout: 600, 50 | }.freeze 51 | -------------------------------------------------------------------------------- /test/cookbooks/ms_dotnet-test/Berksfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | source 'https://supermarket.chef.io' 4 | 5 | metadata 6 | 7 | cookbook 'ms_dotnet', path: '../../../' 8 | -------------------------------------------------------------------------------- /test/cookbooks/ms_dotnet-test/metadata.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | name 'ms_dotnet-test' 4 | maintainer 'Criteo' 5 | maintainer_email 'b.courtois@criteo.com' 6 | license 'Apache-2.0' 7 | description 'Test cookbook for ms_dotnet' 8 | version '0.0.0' 9 | supports 'windows' 10 | depends 'ms_dotnet' 11 | -------------------------------------------------------------------------------- /test/cookbooks/ms_dotnet-test/recipes/framework_install.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # 4 | # Cookbook:: ms_dotnet-test 5 | # Recipe:: framework_install 6 | # Author:: Baptiste Courtois () 7 | # 8 | # Copyright:: (C) 2015-2016, Criteo. 9 | # 10 | # Licensed under the Apache License, Version 2.0 (the "License"); 11 | # you may not use this file except in compliance with the License. 12 | # You may obtain a copy of the License at 13 | # 14 | # http://www.apache.org/licenses/LICENSE-2.0 15 | # 16 | # Unless required by applicable law or agreed to in writing, software 17 | # distributed under the License is distributed on an "AS IS" BASIS, 18 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 19 | # See the License for the specific language governing permissions and 20 | # limitations under the License. 21 | # 22 | fwk_info = node['ms_dotnet_test']['framework_install'] 23 | ms_dotnet_framework 'install' do 24 | version fwk_info['version'] 25 | timeout fwk_info['timeout'] unless fwk_info['timeout'].nil? 26 | include_patches fwk_info['include_patches'] unless fwk_info['include_patches'].nil? 27 | feature_source fwk_info['feature_source'] unless fwk_info['feature_source'].nil? 28 | perform_reboot fwk_info['perform_reboot'] unless fwk_info['perform_reboot'].nil? 29 | package_sources fwk_info['package_sources'] unless fwk_info['package_sources'].nil? 30 | require_support fwk_info['require_support'] unless fwk_info['require_support'].nil? 31 | end 32 | -------------------------------------------------------------------------------- /test/cookbooks/ms_dotnet-test/recipes/reboot.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # 4 | # Cookbook:: ms_dotnet-test 5 | # Recipe:: reboot 6 | # Author:: Baptiste Courtois () 7 | # 8 | # Copyright:: (C) 2015-2016, Criteo. 9 | # 10 | # Licensed under the Apache License, Version 2.0 (the "License"); 11 | # you may not use this file except in compliance with the License. 12 | # You may obtain a copy of the License at 13 | # 14 | # http://www.apache.org/licenses/LICENSE-2.0 15 | # 16 | # Unless required by applicable law or agreed to in writing, software 17 | # distributed under the License is distributed on an "AS IS" BASIS, 18 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 19 | # See the License for the specific language governing permissions and 20 | # limitations under the License. 21 | # 22 | ms_dotnet_reboot 'now' do 23 | action node['ms_dotnet_test']['reboot']['action'] 24 | end 25 | --------------------------------------------------------------------------------