{"id":13558543,"url":"https://github.com/nemonik/hands-on-DevOps","last_synced_at":"2025-04-03T13:31:45.433Z","repository":{"id":49808116,"uuid":"159888240","full_name":"nemonik/hands-on-DevOps","owner":"nemonik","description":"A hands-on DevOps course covering the culture, methods and repeated practices of modern software development involving Packer, Vagrant, VirtualBox, Ansible, Kubernetes, K3s, MetalLB, Traefik, Docker-Compose, Docker, Taiga, GitLab, Drone CI, SonarQube, Selenium, InSpec, Alpine 3.10, Ubuntu-bionic, CentOS 7...","archived":true,"fork":false,"pushed_at":"2021-09-05T22:34:04.000Z","size":80268,"stargazers_count":370,"open_issues_count":0,"forks_count":154,"subscribers_count":19,"default_branch":"master","last_synced_at":"2024-10-01T06:24:06.907Z","etag":null,"topics":["ansible","ansible-automation","continuous-delivery","continuous-integration","devops","docker","docker-image","drone","gitlab","go","hands-on-devops-course","inspec","kubernetes","pipeline","plantuml","selenium","sonarqube","taiga","vagrant","virtual-machine"],"latest_commit_sha":null,"homepage":"","language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nemonik.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-11-30T23:07:02.000Z","updated_at":"2024-09-15T03:32:26.000Z","dependencies_parsed_at":"2022-08-12T20:40:40.740Z","dependency_job_id":null,"html_url":"https://github.com/nemonik/hands-on-DevOps","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nemonik%2Fhands-on-DevOps","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nemonik%2Fhands-on-DevOps/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nemonik%2Fhands-on-DevOps/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nemonik%2Fhands-on-DevOps/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nemonik","download_url":"https://codeload.github.com/nemonik/hands-on-DevOps/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":222970807,"owners_count":17066280,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ansible","ansible-automation","continuous-delivery","continuous-integration","devops","docker","docker-image","drone","gitlab","go","hands-on-devops-course","inspec","kubernetes","pipeline","plantuml","selenium","sonarqube","taiga","vagrant","virtual-machine"],"created_at":"2024-08-01T12:05:00.959Z","updated_at":"2024-11-04T09:31:06.058Z","avatar_url":"https://github.com/nemonik.png","language":"Shell","funding_links":["https://www.buymeacoffee.com/nemonik"],"categories":["Shell","HarmonyOS","go","ansible"],"sub_categories":["Windows Manager"],"readme":"![Basic role syntax check](https://github.com/nemonik/hands-on-DevOps/workflows/Basic%20role%20syntax%20check/badge.svg)\n[![GitHub license](https://img.shields.io/github/license/nemonik/hands-on-DevOps)](./LICENSE)\n[![GitHub Forks](https://img.shields.io/github/forks/nemonik/hands-on-DevOps?style=social)](https://github.com/nemonik/hands-on-DevOps/network/members)\n[![Github Stars](https://img.shields.io/github/stars/nemonik/hands-on-DevOps?style=social)](https://github.com/nemonik/hands-on-DevOps/stargazers)\n[![Twitter](https://img.shields.io/twitter/url?style=social\u0026url=https%3A%2F%2Fgithub.com%2Fnemonik%2Fhands-on-DevOps)](https://twitter.com/intent/tweet?text=I%27ve%20found%20a%20nifty%20hands-on%20DevOps%20course%20developed%20by%20%40nemonik%20at%20https%3A%2F%2Fgithub.com%2Fnemonik%2Fhands-on-DevOps)\n\u003ca href=\"https://www.buymeacoffee.com/nemonik\" target=\"_blank\"\u003e\u003cimg src=\"https://cdn.buymeacoffee.com/buttons/default-orange.png\" alt=\"Buy Me A Coffee\" height=\"20\" width=\"85\"\u003e\u003c/a\u003e\n\n# 1. Preface\n\nThis Vagrant-based version of my Hands-on DevOps class is DEPRECATED.  I suggest you hop over to the new version of this class that significantly modifies the factory's configuration... It is very likely portions of this class IaC will fail at this time. I cannot create the new content while also supporting the prior.  The new class can be found at https://github.com/nemonik/hands-on-DevOps-gen2.\n\n# 2. DevOps\n\nA hands-on DevOps course covering the culture, methods and repeated practices of modern software development involving Vagrant, VirtualBox, Ansible, Kubernetes, K3s, MetalLB, Traefik, Docker-Compose, Docker, Taiga, GitLab, Drone CI, SonarQube, Selenium, InSpec, Alpine 3.10, Ubuntu-bionic, CentOS 7...\n\nA reveal.js presentation written to accompany this course can found at [https://nemonik.github.io/hands-on-DevOps/](https://nemonik.github.io/hands-on-DevOps/).\n\nThis course will\n\n1. Discuss DevOps,\n2. Have you spin up a DevOps toolchain and development environment, and then\n3. Author two applications and their accompanying pipelines, the first a continuous integration (CI) and the second a continuous delivery (CD) pipeline.\n\nAfter this course, you will\n\n1. Be able to describe and have hands-on experience DevOps methods and repeated practices (e.g., use of Agile methods, configuration management, build automation, test automation and deployment automation orchestrated under a CICD orchestrator), and why it matters;\n2. Address challenges transitioning to DevOps methods and repeated practices;\n3. Have had hands-on experience with Infrastructure as Code( Vagrant and Ansible ) to provision and configure an entire DevOps Factory (i.e. a toolchain and development environment) on VirtualBox including Docker Registry, Taiga, GitLab, Drone CI, and SonarQube;\n4. Have had hands-on experience authoring code to include authoring and running automated tests in a CICD pipeline all under Configuration Management to ensure an application follows style, adheres to good coding practices, builds, identify security issues, and functions as expected;\n5. Have had hands-on experience with\n    1. using Infrastructure as Code (IaC) in Vagrant and Ansible;\n    2. creating and using Kanban board in Taiga;\n    3. code configuration in git and GitLab;\n    4. authoring code in Go;\n    5. using style checkers and linters;\n    6. authoring a Makefile;\n    7. various commands in Docker (e.g., building a container image, pushing a container into a registry, creating and running a container);\n    8. authoring a pipeline for Drone CI;\n    9. using Sonar Scanner CLI to perform static analysis;\n    10. authoring security test in InSpec;\n    11. author an automated functional test in Selenium;\n    12. authoring a dynamic security test in OWASP Zap; and\n    13. using container platform to author and scale services;\n6. Have had hands-on experience authoring code to include authoring and running automated tests in a CICD pipeline all under Configuration Management to ensure an application follows style, adheres to good coding practices, builds, identify security issues, and functions as expected.\n\nWe will be spending most of the course hands-on working with the tools and in the Unix command line making methods and repeated practices of DevOps happen, so as to grow an understanding of how DevOps actually works. Although, not necessary I would encourage you to pick up a free PDF of [The Linux Command Line by William Shotts](http://linuxcommand.org/tlcl.php) if you are no familiar wit the Linux command line.\n\nDon't fixate on the tools used, nor the apps we develop in the course of learning how and why. How and why is far more important. This course like DevOps is not about tools although we'll be using them. You'll spend far more time writing code. (Or at the very least cutting-and-pasting code.)\n\n# 3. Author\n\n- Michael Joseph Walsh [mjwalsh@mitre.org](mailto:mjwalsh@mitre.org), [walsh@nemonik.com](mailto:walsh@nemonik.com)\n\n# 4. Copyright and license\n\nSee the [License file](LICENCE) at the root of this project.\n\n# 5. Prerequisites\n\nThe following skills would be useful in following along but aren't strictly necessary.\n\nWhat you should bring:\n\n- Managing Linux or Unix-like systems would be tremendously helpful, but not necessary, as we will be living largely within the terminal.\n- A basic understanding of Vagrant, Docker, and Ansible would also be helpful, but not necessary.\n\n# 6. Table of Contents\n\n\u003c!-- TOC --\u003e\n\n- [1. Preface](#1-preface)\n- [2. DevOps](#2-devops)\n- [3. Author](#3-author)\n- [4. Copyright and license](#4-copyright-and-license)\n- [5. Prerequisites](#5-prerequisites)\n- [6. Table of Contents](#6-table-of-contents)\n- [7. DevOps unpacked](#7-devops-unpacked)\n    - [7.1. What is DevOps?](#71-what-is-devops)\n    - [7.2. What DevOps is not](#72-what-devops-is-not)\n    - [7.3. To succeed at DevOps you must](#73-to-succeed-at-devops-you-must)\n    - [7.4. If your effort doesn't](#74-if-your-effort-doesnt)\n    - [7.5. Conway's Law states](#75-conways-law-states)\n    - [7.6. DevOps is really about](#76-devops-is-really-about)\n    - [7.7. What is DevOps culture?](#77-what-is-devops-culture)\n        - [7.7.1. We were taught the requisite skills as children](#771-we-were-taught-the-requisite-skills-as-children)\n        - [7.7.2. Maintaining relationships is your most important skill](#772-maintaining-relationships-is-your-most-important-skill)\n        - [7.7.3. Be quick... Be slow to...](#773-be-quick-be-slow-to)\n        - [7.7.4. The pressures of social media](#774-the-pressures-of-social-media)\n    - [7.8. How is DevOps related to the Agile?](#78-how-is-devops-related-to-the-agile)\n    - [7.9. How do they differ?](#79-how-do-they-differ)\n    - [7.10. Why?](#710-why)\n    - [7.11. What are the principles of DevOps?](#711-what-are-the-principles-of-devops)\n    - [7.12. Much of this is achieved](#712-much-of-this-is-achieved)\n    - [7.13. What is Continuous Integration (CI)?](#713-what-is-continuous-integration-ci)\n    - [7.14. How?](#714-how)\n    - [7.15. CI best practices](#715-ci-best-practices)\n        - [7.15.1. Utilize a Configuration Management System](#7151-utilize-a-configuration-management-system)\n        - [7.15.2. Automate the build](#7152-automate-the-build)\n        - [7.15.3. Employ one or more CI services/orchestrators](#7153-employ-one-or-more-ci-servicesorchestrators)\n        - [7.15.4. Make builds self-testing](#7154-make-builds-self-testing)\n        - [7.15.5. Never commit broken](#7155-never-commit-broken)\n        - [7.15.6. Stakeholders are expected to pre-flight new code](#7156-stakeholders-are-expected-to-pre-flight-new-code)\n        - [7.15.7. The CI service/orchestrator provides feedback](#7157-the-ci-serviceorchestrator-provides-feedback)\n    - [7.16. What is Continuous Delivery?](#716-what-is-continuous-delivery)\n        - [7.16.1. Extending Continuous Integration (CI)](#7161-extending-continuous-integration-ci)\n        - [7.16.2. Consistency](#7162-consistency)\n    - [7.17. But wait. What's a pipeline?](#717-but-wait-whats-a-pipeline)\n    - [7.18. How is a pipeline manifested?](#718-how-is-a-pipeline-manifested)\n    - [7.19. What underlines all of this?](#719-what-underlines-all-of-this)\n    - [7.20. But really why do we automate err. code?](#720-but-really-why-do-we-automate-err-code)\n        - [7.20.1. Why do I mention Larry Wall?](#7201-why-do-i-mention-larry-wall)\n        - [7.20.2. Laziness](#7202-laziness)\n        - [7.20.3. Impatience](#7203-impatience)\n        - [7.20.4. Hubris](#7204-hubris)\n        - [7.20.5. We automate for](#7205-we-automate-for)\n    - [7.21. Monitoring](#721-monitoring)\n        - [7.21.1. The _primary_ metric](#7211-the-_primary_-metric)\n        - [7.21.2. An understanding of performance](#7212-an-understanding-of-performance)\n        - [7.21.3. Establish a baseline performance](#7213-establish-a-baseline-performance)\n        - [7.21.4. Set reaction thresholds](#7214-set-reaction-thresholds)\n        - [7.21.5. Reacting](#7215-reacting)\n        - [7.21.6. Gaps in CICD](#7216-gaps-in-cicd)\n        - [7.21.7. Eliminating waste](#7217-eliminating-waste)\n    - [7.22. Crawl, walk, run](#722-crawl-walk-run)\n        - [7.22.1. Ultimately, DevOps is Goal](#7221-ultimately-devops-is-goal)\n- [8. Reading list](#8-reading-list)\n- [9. Now the hands-on part](#9-now-the-hands-on-part)\n    - [9.1. Configuring environmental variables](#91-configuring-environmental-variables)\n    - [9.2. VirtualBox](#92-virtualbox)\n        - [9.2.1. Installing VirtualBox](#921-installing-virtualbox)\n    - [9.3. Git Bash](#93-git-bash)\n        - [9.3.1. Installing Git Bash](#931-installing-git-bash)\n    - [9.4. Retrieve the course material](#94-retrieve-the-course-material)\n    - [9.5. Infrastructure as code (IaC)](#95-infrastructure-as-code-iac)\n        - [9.5.1. Hashicorp Packer](#951-hashicorp-packer)\n            - [9.5.1.1. Packer document and source](#9511-packer-document-and-source)\n            - [9.5.1.2. Installing Packer](#9512-installing-packer)\n            - [9.5.1.3. Packer project explained](#9513-packer-project-explained)\n            - [9.5.1.4. Packer execution](#9514-packer-execution)\n        - [9.5.2. Vagrant](#952-vagrant)\n            - [9.5.2.1. Vagrant documentation and source](#9521-vagrant-documentation-and-source)\n            - [9.5.2.2. Installing Vagrant](#9522-installing-vagrant)\n            - [9.5.2.3. The Vagrantfile explained](#9523-the-vagrantfile-explained)\n                - [9.5.2.3.1. Modelines](#95231-modelines)\n                - [9.5.2.3.2. Setting extra variables for Ansible roles](#95232-setting-extra-variables-for-ansible-roles)\n                - [9.5.2.3.3. Automatically installing and removing the necessary Vagrant plugins](#95233-automatically-installing-and-removing-the-necessary-vagrant-plugins)\n                - [9.5.2.3.4. Inserting Proxy setting via host environmental variables](#95234-inserting-proxy-setting-via-host-environmental-variables)\n                - [9.5.2.3.5. Inserting enterprise CA certificates](#95235-inserting-enterprise-ca-certificates)\n                - [9.5.2.3.6. Auto-generate the Ansible inventory file](#95236-auto-generate-the-ansible-inventory-file)\n                - [9.5.2.3.7. Mounting the project folder into each vagrant](#95237-mounting-the-project-folder-into-each-vagrant)\n                - [9.5.2.3.8. Build a Vagrant Box](#95238-build-a-vagrant-box)\n                - [9.5.2.3.9. Configuring the Kubernetes cluster vagrant(s)](#95239-configuring-the-kubernetes-cluster-vagrants)\n                - [9.5.2.3.10. Provisioning and configuring the *development* vagrant](#952310-provisioning-and-configuring-the-development-vagrant)\n        - [9.5.3. Ansible](#953-ansible)\n            - [9.5.3.1. Playbooks](#9531-playbooks)\n                - [9.5.3.1.1. The `master` vagrant's playbook](#95311-the-master-vagrants-playbook)\n                - [9.5.3.1.2. The `worker` vagrant's playbook](#95312-the-worker-vagrants-playbook)\n                - [9.5.3.1.3. The `development` playbooks](#95313-the-development-playbooks)\n            - [9.5.3.2. Roles](#9532-roles)\n            - [9.5.3.3. Bringing all the vagrants up](#9533-bringing-all-the-vagrants-up)\n    - [9.6. The cloud-native technologies underlying the tools](#96-the-cloud-native-technologies-underlying-the-tools)\n        - [9.6.1. Docker image and containers](#961-docker-image-and-containers)\n        - [9.6.2. Docker-compose](#962-docker-compose)\n        - [9.6.3. Kubernetes](#963-kubernetes)\n            - [9.6.3.1. K3s, light-weight Kubernetes](#9631-k3s-light-weight-kubernetes)\n            - [9.6.3.2. Kubectl, the Kubernetes command-line tool](#9632-kubectl-the-kubernetes-command-line-tool)\n            - [9.6.3.3. Kubernetes-Dashboard](#9633-kubernetes-dashboard)\n    - [9.7. The long-running tools](#97-the-long-running-tools)\n        - [9.7.1. Taiga, an example of Agile project management software](#971-taiga-an-example-of-agile-project-management-software)\n            - [9.7.1.1. Documentation, source, container image](#9711-documentation-source-container-image)\n            - [9.7.1.2. URL, Username and password](#9712-url-username-and-password)\n        - [9.7.2. GitLab CE, an example of configuration management software](#972-gitlab-ce-an-example-of-configuration-management-software)\n            - [9.7.2.1. Documentation, source, container image](#9721-documentation-source-container-image)\n            - [9.7.2.2. URL, Username and password](#9722-url-username-and-password)\n            - [9.7.2.3. Notes in regards to the class GitLab](#9723-notes-in-regards-to-the-class-gitlab)\n        - [9.7.3. Drone CI, an example of CICD orchestrator](#973-drone-ci-an-example-of-cicd-orchestrator)\n            - [9.7.3.1. Documentation, source, container image](#9731-documentation-source-container-image)\n            - [9.7.3.2. URL, Username and password](#9732-url-username-and-password)\n        - [9.7.4. SonarQube, an example of a platform for the inspection of code quality](#974-sonarqube-an-example-of-a-platform-for-the-inspection-of-code-quality)\n            - [9.7.4.1. Documentation, source, container image](#9741-documentation-source-container-image)\n            - [9.7.4.2. URL, Username and password](#9742-url-username-and-password)\n        - [9.7.5. PlantUML Server, an example of light-weight documentation](#975-plantuml-server-an-example-of-light-weight-documentation)\n            - [9.7.5.1. Documentation, source, container image](#9751-documentation-source-container-image)\n            - [9.7.5.2. URL](#9752-url)\n    - [9.8. Golang _helloworld_ project](#98-golang-_helloworld_-project)\n        - [9.8.1. Create the project's backlog](#981-create-the-projects-backlog)\n        - [9.8.2. Create the project in GitLab](#982-create-the-project-in-gitlab)\n        - [9.8.3. Setup the project on the _development_ Vagrant](#983-setup-the-project-on-the-_development_-vagrant)\n        - [9.8.4. Author the application](#984-author-the-application)\n        - [9.8.5. Align source code with Go coding standards](#985-align-source-code-with-go-coding-standards)\n        - [9.8.6. Lint your code](#986-lint-your-code)\n        - [9.8.7. Build the application](#987-build-the-application)\n        - [9.8.8. Run your application](#988-run-your-application)\n        - [9.8.9. Author the unit tests](#989-author-the-unit-tests)\n        - [9.8.10. Automate the build (i.e., write the Makefile)](#9810-automate-the-build-ie-write-the-makefile)\n        - [9.8.11. Author Drone-based Continuous Integration](#9811-author-drone-based-continuous-integration)\n            - [9.8.11.1. Configure Drone to execute your pipeline](#98111-configure-drone-to-execute-your-pipeline)\n            - [9.8.11.2. Trigger the build](#98112-trigger-the-build)\n        - [9.8.12. The completed source for *helloworld*](#9812-the-completed-source-for-helloworld)\n    - [9.9. Golang *helloworld-web* project](#99-golang-helloworld-web-project)\n        - [9.9.1. Create the project's backlog](#991-create-the-projects-backlog)\n        - [9.9.2. Create the project in GitLab](#992-create-the-project-in-gitlab)\n        - [9.9.3. Setup the project on the _development_ Vagrant](#993-setup-the-project-on-the-_development_-vagrant)\n        - [9.9.4. Author the application](#994-author-the-application)\n        - [9.9.5. Build and run the application](#995-build-and-run-the-application)\n        - [9.9.6. Run gometalinter.v2 on application](#996-run-gometalinterv2-on-application)\n        - [9.9.7. Fix the application](#997-fix-the-application)\n        - [9.9.8. Author unit tests](#998-author-unit-tests)\n        - [9.9.9. Perform static analysis (i.e., sonar-scanner) on the command line](#999-perform-static-analysis-ie-sonar-scanner-on-the-command-line)\n            - [9.9.9.1. Optionally, register your app in SonarQube](#9991-optionally-register-your-app-in-sonarqube)\n            - [9.9.9.2. Install the SonarGo plugin](#9992-install-the-sonargo-plugin)\n            - [9.9.9.3. Perform static analysis (run *sonar-scanner*) on the command line](#9993-perform-static-analysis-run-sonar-scanner-on-the-command-line)\n        - [9.9.10. Automate the build (i.e., write the Makefile)](#9910-automate-the-build-ie-write-the-makefile)\n        - [9.9.11. Dockerize the application](#9911-dockerize-the-application)\n        - [9.9.12. Run the Docker container](#9912-run-the-docker-container)\n            - [9.9.12.1. Option 1](#99121-option-1)\n            - [9.9.12.2. Option 2](#99122-option-2)\n        - [9.9.13. Push the container image to the private Docker registry](#9913-push-the-container-image-to-the-private-docker-registry)\n        - [9.9.14. Configure Drone to execute your CICD pipeline](#9914-configure-drone-to-execute-your-cicd-pipeline)\n        - [9.9.15. Add Static Analysis (*SonarQube*) step to pipeline](#9915-add-static-analysis-sonarqube-step-to-pipeline)\n        - [9.9.16. Add the build step to the pipeline](#9916-add-the-build-step-to-the-pipeline)\n        - [9.9.17. Add container image publish step to pipeline](#9917-add-container-image-publish-step-to-pipeline)\n        - [9.9.18. Add container deploy step to pipeline](#9918-add-container-deploy-step-to-pipeline)\n        - [9.9.19. Add compliance and policy automation (InSpec) test to the pipeline](#9919-add-compliance-and-policy-automation-inspec-test-to-the-pipeline)\n            - [9.9.19.1. First author an InSpec test](#99191-first-author-an-inspec-test)\n            - [9.9.19.2. Execute your test](#99192-execute-your-test)\n            - [9.9.19.3. Add InSpec to the pipeline](#99193-add-inspec-to-the-pipeline)\n            - [9.9.19.4. Viewing the results in Heimdall-lite](#99194-viewing-the-results-in-heimdall-lite)\n        - [9.9.20. Add automated functional test to pipeline](#9920-add-automated-functional-test-to-pipeline)\n            - [9.9.20.1. Run the *helloworld-web* application](#99201-run-the-helloworld-web-application)\n            - [9.9.20.2. Pull and run Selenium Firefox Standalone](#99202-pull-and-run-selenium-firefox-standalone)\n            - [9.9.20.3. Create our test automation](#99203-create-our-test-automation)\n            - [9.9.20.4. Enable `Trusted` for the repository in Drone](#99204-enable-trusted-for-the-repository-in-drone)\n            - [9.9.20.5. Add a *selenium* step to the pipeline](#99205-add-a-selenium-step-to-the-pipeline)\n        - [9.9.21. Add DAST step (*OWASP ZAP*) to pipeline](#9921-add-dast-step-owasp-zap-to-pipeline)\n        - [9.9.22. All the source for *helloworld-web*](#9922-all-the-source-for-helloworld-web)\n    - [9.10. Additional best practices to consider around securing containerized applications](#910-additional-best-practices-to-consider-around-securing-containerized-applications)\n    - [9.11. Microservices](#911-microservices)\n        - [9.11.1. What's cloud-native?](#9111-whats-cloud-native)\n        - [9.11.2. Let's create a microservice](#9112-lets-create-a-microservice)\n            - [9.11.2.1. Modify the helloworld-web application](#91121-modify-the-helloworld-web-application)\n            - [9.11.2.2. Create a Kubernetes manifest for the microservice](#91122-create-a-kubernetes-manifest-for-the-microservice)\n            - [9.11.2.3. Deploy your application](#91123-deploy-your-application)\n            - [9.11.2.4. Test your microservice](#91124-test-your-microservice)\n            - [9.11.2.5. Scale your microservice](#91125-scale-your-microservice)\n    - [9.12. Using what you've learned](#912-using-what-youve-learned)\n    - [9.13. Shoo away your vagrants](#913-shoo-away-your-vagrants)\n    - [9.14. That's it](#914-thats-it)\n\n\u003c!-- /TOC --\u003e\n\n# 7. DevOps unpacked\n\n## 7.1. What is DevOps?\n\nDevOps (a clipped compound of the words *development* and *operations*) is a software development methodology with an emphasis on a reliable release pipeline, automation, and stronger collaboration across all stakeholders with the goal of delivery of value in close alignment with business objectives into the hands of users (i.e., production) more efficiently and effectively.\n\n*Ops* in DevOps gathers up every IT operation stakeholders (i.e., cybersecurity, testing, DB admin, infrastructure and operations practitioners -- essentially, any stakeholder not commonly thought of as directly part of the development team in the system development life cycle).\n\nYeah, that's the formal definition.\n\nIn the opening sentences of _Security Engineering: : A Guide to Building Dependable Distributed Systems — Third Edition_, author Ross Anderson defines what a security engineer is\n\n\u003e Security engineering is about building systems to remain dependable in the face of malice, error, or mischance. As a discipline, it focuses on the tools, processes, and methods needed to design, implement, and test complete systems, and to adapt existing systems as their environment evolve.\n\nThe words _security engineering_ could be replaced in the opening sentence with each one of the various stakeholders (e.g., development, quality assurance, technology operations).\n\nThe point I'm after is everyone is in it to collectively deliver dependable software.\n\nAlso, there is no need to overload the _DevOps_ term -- To _Dev wildcard (i.e., *) Ops_ to include your pet interest(s), such as, _security_, _test_, _whatever_... to form _DevSecOps_, _DevTestOps_, _DevWhateverOps_...  _DevOps_ has you covered.\n\n## 7.2. What DevOps is not\n\nAbout the tools.\n\n![My Pillow Guy](./images/my_pillow_guy.png)\n\n\u003csub\u003eThere are countless vendors out there, who want to sell you their crummy tool.\u003c/sub\u003e\n\n## 7.3. To succeed at DevOps you must\n\n__Combine software development and information technology operations in the systems development life cycle__ with __a focus on collaboration across the life cycle to deliver features, fixes, and updates frequently in close alignment with business objectives__.\n\nIf the effort cannot combine both Dev and Ops in collaboration with this focus the effort will most certainly fail.\n\n## 7.4. If your effort doesn't\n\n__grok (i.e, Understand intuitively) what DevOps is in practice__ and have performed the __necessary analysis of the existing culture and a strategy for how to affect a change__ the effort again will likely fail.\n\nI say this because the culture is the largest influencer over the success of both Agile and DevOps and ultimately the path taken (i.e., plans made.)\n\n## 7.5. Conway's Law states\n\n\u003e Any organization that designs a system (defined broadly) will produce a design whose structure is a copy of the organization's communication structure.\n\nFrom [\"How Do Committees Invent?\"](http://www.melconway.com/Home/Conways_Law.html)\n\nFollowed with\n\n\u003e Ways must be found to reward design managers for keeping their organizations lean and flexible.\n\nThis was written over 50 years ago.\n\nIf your communication structure is broke, so shall your systems be.\n\n## 7.6. DevOps is really about\n\nProviding the culture, methods and repeated practices to permit stakeholders to collaborate.\n\n## 7.7. What is DevOps culture?\n\n\u003e **culture** noun  \\ *ˈkəl-chər* \\\n\u003e the set of shared attitudes, values, goals, and practices that characterizes an institution or organization\n\nI love when a word means precisely what you need it to mean.\n\nWith the stakeholders sharing the same attitudes, values, goals, using the same tools, methods and repeated practices for their particular discipline you have ***DevOps Culture***.\n\n### 7.7.1. We were taught the requisite skills as children\n\n![Paw Patrol](./images/paw_patrol.png)\n\n### 7.7.2. Maintaining relationships is your most important skill\n\n![Maintaining Relationships](./images/maintaining_relationships.png)\n\n### 7.7.3. Be quick... Be slow to...\n\n![Quick to slow](./images/quick_to_slow_to.png)\n\n### 7.7.4. The pressures of social media\n\n![Social Media](./images/social_media.png)\n\n## 7.8. How is DevOps related to the Agile?\n\nAgile Software Development is an umbrella term for a set of methods and practices based on the [values](http://www.agilealliance.org/agile101/the-agile-manifesto/) and [principles](http://www.agilealliance.org/agile101/12-principles-behind-the-agile-manifesto/) expressed in the Agile Manifesto.\n\nFor Agile, solutions evolve through collaboration between self-organizing, cross-functional teams utilizing the appropriate practices for their context.\n\nDevOps builds on this.\n\n## 7.9. How do they differ?\n\nWhile DevOps extends Agile methods and practices by adding communication and collaboration between\n\n- development,\n- security,\n- quality assurance, and\n- technology operations\n\nfunctionaries as stakeholders into the broader effort to ensure software systems are delivered in a reliable, low-risk manner.\n\n## 7.10. Why?\n\nIn Agile Software Development, there is rarely an integration of these individuals outside the immediate application development team with members of technology operations (e.g., network engineers, administrators, testers, security engineers.)\n\n## 7.11. What are the principles of DevOps?\n\nAs DevOps matures, several principles have emerged, namely the necessity for product teams to:\n\n- Apply holistic thinking to solve problems,\n- Develop and test against production-like environments,\n- Deploy with repeatable and reliable processes,\n- Remove the drudgery and uncertainty through automation,\n- Validate and monitor operational quality, and\n- Provide rapid, automated feedback to the stakeholders\n\n## 7.12. Much of this is achieved\n\nThrough the repeated practices of Continuous Integration (CI) and Continuous Delivery (CD) often conflated into simply \"CI/CD\" or \"CICD\".\n\nWARNING: After tools, CICD is the next (**albeit mistakenly**) thing thought to be the totality of DevOps.\n\n## 7.13. What is Continuous Integration (CI)?\n\nIt is a repeated Agile software development practice lifted specifically from Extreme programming, where members of a development team frequently integrate their work to detect integration issues as quickly as possible thereby shifting discovery of issues \"left\" (i.e., early) in the software release.\n\n## 7.14. How?\n\nEach integration is orchestrated through a CI service/orchestrator (e.g., Jenkins CI, Drone CI, GitLab Runners, Concourse CI) that essentially assembles a build, runs unit and integration tests every time a predetermined trigger has been met; and then reports with immediate feedback.\n\n## 7.15. CI best practices\n\n### 7.15.1. Utilize a Configuration Management System\n\nFor the software's source code, where the mainline (i.e., master branch) is the most recent working version, past releases held in branches, and new features not yet merged into the mainline branch worked in their own branches.\n\n### 7.15.2. Automate the build\n\nBy accompanying build automation (e.g., Gradle, Apache Maven, Make) alongside the source code.\n\n### 7.15.3. Employ one or more CI services/orchestrators\n\nTo perform source code analysis via automating formal code inspection and assessment.\n\n### 7.15.4. Make builds self-testing\n\nIn other words, ingrain testing by including unit and integration tests (e.g., Spock, JUnit, Mockito, SOAPUI, go package *Testing*) with the source code to be executed by the build automation to be executed by the CI service.\n\n### 7.15.5. Never commit broken\n\nOr untested source code to the CMS mainline or otherwise risk breaking a build.\n\n### 7.15.6. Stakeholders are expected to pre-flight new code\n\nPrior to committing source code in their own workspace.\n\n### 7.15.7. The CI service/orchestrator provides feedback\n\nOn the success or fail of a build integration to all its stakeholders.\n\n## 7.16. What is Continuous Delivery?\n\nIt is a repeated software development practice of providing a rapid, reliable, low-risk product delivery achieved through automating all facets of building, testing, and deploying software.\n\n### 7.16.1. Extending Continuous Integration (CI)\n\nWith additional stages/steps aimed to provide ongoing validation that a newly assembled software build meets all desired requirements and thereby is releasable.\n\n### 7.16.2. Consistency\n\nIs achieved through delivering applications into production via individual repeatable pipelines of ingrained system configuration management and testing\n\n## 7.17. But wait. What's a pipeline?\n\nA pipeline automates the various stages/steps (e.g., Static Application Security Testing (SAST), build, unit testing, Dynamic Application Security Testing (DAST), secure configuration acceptance compliance, integration, function and non-functional testing, delivery, and deployment) to enforce quality conformance.\n\n## 7.18. How is a pipeline manifested?\n\nEach delivery pipeline is manifested as **Pipeline as Code** (i.e., software automation) accompanying the application's source code in its version control repository.\n\n## 7.19. What underlines all of this?\n\nI and the community of practice argue DevOps will struggle without ubiquitous access to shared pools of software configurable system resources and higher-level services that can be rapidly provisioned (i.e., cloud).\n\nAlthough, it is actually possible to [DevOps on mainframes](https://www.youtube.com/watch?v=eMS97X5ZTGc) The video is in the contect of continuous delivery, but read between the lines.\n\n## 7.20. But really why do we automate err. code?\n\nIn 2001, I think Larry Wall in his 1st edition of *Programming Perl* book put it best with \"We will encourage you to develop the three great virtues of a programmer:\n\nlaziness,\n\nimpatience, and\n\nhubris.\"\n\nThe second edition of the same book provided definitions for these terms\n\n### 7.20.1. Why do I mention Larry Wall?\n\nWell...\n\n\u003e _Once you have established yourself as an icon in your field it is important that you pay tribute  to some of the great legends that came before you. This kind of gesture will create the illusion that you’re still humble and serve as a preemptive strike against anyone who has noticed what a callus and delusional ass you have become._\n\u003e\n\u003e From the opening monologue to the Blue Man Group’s _I Feel Love_ https://www.youtube.com/watch?v=8vBKI3ya-l0\n\nI kid, but in all serious the sentiment of this seminal, nearly twenty-something year-old book still holds true.\n\nLet me explain.\n\n### 7.20.2. Laziness\n\n\u003e The quality that makes you go to great effort to reduce overall energy expenditure. It makes you write labor-saving programs that other people will find useful, and document what you wrote so you don't have to answer so many questions about it. Hence, the first great virtue of a programmer._ (p.609)\n\n### 7.20.3. Impatience\n\n\u003e The anger you feel when the computer is being lazy. This makes you write programs that don't just react to your needs, but actually anticipate them. Or at least pretend to. Hence, the second great virtue of a programmer._ (p.608)\n\n### 7.20.4. Hubris\n\n\u003e Excessive pride, the sort of thing Zeus zaps you for. Also, the quality that makes you write (and maintain) programs that other people won't want to say bad things about. Hence, the third great virtue of a programmer._ (p.607)\n\n### 7.20.5. We automate for\n\n- Faster, coordinated, repeatable, and therefore more reliable deployments.\n- Discover bugs sooner. Shifting their discovery left in the process.\n- To accelerates the feedback loop between Dev and Ops (Again, Ops is everyone not typically considered part of the development team.)\n- Reduce tribal knowledge, where one group or person holds the keys to how things get done. Yep, this is about making us all replaceable.\n- Reduce shadow IT (i.e., hardware or software within an enterprise that is not supported by IT. Just waiting for its day to explode.)\n\n## 7.21. Monitoring\n\nOnce deployed, the work is done, right?\n\nSo, that improvements can be gauged and anomalies detected.A development team's work is not complete once a product leaves CICD and enters production; especially, under DevOps where the development team includes members of ops (e.g., security and technology operations).\n\n### 7.21.1. The _primary_ metric\n\nIs working software, but this is not the only, measurement.  The key to successful DevOps is knowing how well the methodology and the software it produces are performing.  Is the software truely dependable?\n\n### 7.21.2. An understanding of performance\n\nIs achieved by collecting and analyzing data produced by environments used for CICD and production.\n\n### 7.21.3. Establish a baseline performance\n\nSo, that improvements can be gauged and anomalies detected.\n\n### 7.21.4. Set reaction thresholds\n\nTo formulate and prioritize reactions weighting factors, such as, the frequency at which an anomaly arises and who is impacted.\n\n### 7.21.5. Reacting\n\nCould be as simple as operations instructing users through training to not do something that triggers the anomaly, or more ideally, result in an issue being entered into the product's backlog culminating in the development team delivering a fix into production.\n\n### 7.21.6. Gaps in CICD\n\nAre surfaces through monitoring resulting in for example additional testing for an issue discovered in prodcuction.\n\nYep. News flash. DevOps will not entirely stop all bugs or vulnerabilities from making it into production, but this was never the point.\n\n### 7.21.7. Eliminating waste\n\nThrough re-scoping of requirements, re-prioritizing of a backlog, or the deprecation of unused features.  Again, all surfaced through monitoring.\n\n## 7.22. Crawl, walk, run\n\n### 7.22.1. Ultimately, DevOps is Goal\n\n- With DevOps one does not simply hit the ground running.\n- One must first crawl, walk and then ultimately run as you embrace the necessary culture change, methods, and repeated practices.\n- Collaboration and automation are expected to continually improve so to achieve more frequent and more reliable releases.\n\n# 8. Reading list\n\n**AntiPatterns: Refactoring Software, Architectures, and Projects in Crisis**\nWilliam J. Brown, Raphael C. Malveau, Hays W. \"Skip\" McCormick,  and Thomas J. Mowbray\nISBN: 978-0-471-19713-3\nApr 1998\n\n**Continuous Delivery: Reliable Software Releases through Build, Test, and Deployment Automation (Addison-Wesley Signature Series (Fowler))**\nDavid Farley and Jez Humble\nISBN-13: 978-0321601919\nAugust 2010\n\n**The DevOps Handbook: How to Create World-Class Agility, Reliability, and Security in Technology Organizations**\nGene Kim Jez Humble,  Patrick Debois, and John Willis\nISBN-13: 978-1942788003\nOctober 2016\n\n**Accelerate: The Science of Lean Software and DevOps: Building and Scaling High Performing Technology Organizations**\nNicole Forsgren PhD, Jez Humble, and Gene Kim\nISBN-13: 978-1942788331\nMarch 27, 2018\n\n**Site Reliability Engineering: How Google Runs Production Systems 1st Edition**\nBetsy Beyer, Chris Jones, Jennifer Petoff, and Niall Richard Murphy\nISBN-13: 978-1491929124\nApril 16, 2016\nAlso, available online at https://landing.google.com/sre/book/index.html\n\n**Release It!: Design and Deploy Production-Ready Software 2nd Edition**\nMichael T. Nygard\nISBN-13: 978-1680502398\nJanuary 18, 2018\n\n**The SPEED of TRUST: The One Thing That Changes Everything**\nStephen M .R. Covey\nISBN-13: 978-1416549000\nFebruary 5, 2008\nThe gist of the book can be found at SlideShare https://www.slideshare.net/nileshchamoli/the-speed-of-trust-13205957\n\n**RELATIONSHIP TRUST: The 13 Behaviors of High-Trust Leaders Mini Session**\nFranklin Covey Co.\nhttps://archive.franklincovey.com/facilitator/minisessions/handouts/13_Behaviors_MiniSession_Handout.pdf\n\n**How to Deal With Difficult People**\nUjjwal Sinha\nOct 25, 2014\nThe SlideShare can be found here https://www.slideshare.net/abhiujjwal/how-2-deal-wid-diiclt-ppl\n\n**Leadership Secrets of the Rouge Warrior: A Commando's Guide to Success**\nRichard Marcinko w/ John Weisman\nISBN-13: 978-0671545154\nJune 1, 1996\n\n**Security Engineering: A Guide To Building Dependable Distributed Systems**\nRoss Anderson\nISBN-13: 978-0470068526\nApril 14, 2008\nThe second edition of this book can be downloaded in whole from https://www.cl.cam.ac.uk/~rja14/book.html and Mr Anderson has released chapters from his 3rd edition under development.\n\n**How Do Committees Invent?**\nMelvin E. Conway\nCopyright 1968, F. D. Thompson Publications, Inc.\nhttp://www.melconway.com/Home/Conways_Law.html\n\n**The Pragmatic Programmer: Your Journey To Mastery, 20th Anniversary Edition (2nd Edition)**\nDavid Thomas and Andrew Hunt\nISBN-13: 978-0135957059\nSeptember 23, 2019\n\n# 9. Now the hands-on part\n\nIn this class, you will spin up a development and toolchain environment.\n\n**NOTE**\n\n- This class makes use of **NOTE** sections to call out things that are important to know or to drop a few tidbits. Reading these notes may save you some aggravation.\n- This diagrams used in this document are authored in PlantUML. Each diagram is authored in a PlantUML domain-specific language (DSL) and can be found in a separate file in the [plantuml folder](plantuml) in the root of the project.  The class project makes use of [GitHub worflow](.github/worflows/main.yml) to render each file into a scalable vector graphic (SVG) placing each in the [diagrams folder](diagrams) also at the root of the class project.\n\n![Deployment Diagram](diagrams/deployment.svg)\n[PlantUML source for this diagram](plantuml/deployment.puml)\n\n## 9.1. Configuring environmental variables\n\nIf your environment makes use of an HTTP proxy or SSL inspection, you will need to configure environment variables for this class.\n\n**On Mac OS X or \\*NIX environments**\n\nThe following `set_env.sh` BASH script is included in the root of the project and can be used to configure the UNIX environment variables, but must be adjusted for your specific environment.\n\n```bash\n#!/usr/bin/env bash\n\n# Copyright (C) 2019 Michael Joseph Walsh - All Rights Reserved\n# You may use, distribute and modify this code under the\n# terms of the the license.\n#\n# You should have received a copy of the license with\n# this file. If not, please email \u003cmjwalsh@nemonik.com\u003e\n\n# run in shell via\n#\n# ```\n# . ./set_env.sh\n# ```\n#\n# will set proxy setting to the the hard-cded value on line 36.\n# Modify for your environment.\n#\n# ```\n# . ./set_env.sh no_proxy\n# ```\n#\n# will unset all proxy related environmental variables.\n\nset_proxy=true\n\nif [ $# -ne 0 ]; then\n  args=(\"$@\")\n  if [[ $args[1] = \"no_proxy\" ]]; then\n    set_proxy=false\n  fi\nfi\n\nif [[ $set_proxy = true ]]; then\n  export PROXY=http://gatekeeper.mitre.org:80\n  echo \"Setting proxy environment varaibles to $PROXY\"\n  export proxy=$PROXY\n  export HTTP_PROXY=$PROXY\n  export http_proxy=$PROXY\n  export HTTPS_PROXT=$PROXY\n  export https_proxy=$PROXY\n  export ALL_PROXY=$PROXY\n  export NO_PROXY=\"127.0.0.1,localhost,.mitre.org,.local,192.168.0.9,192.168.0.10,192.168.0.11,192.168.0.206,192.168.0.10,192.168.0.202,192.168.0.203,192.168.0.204,192.168.0.205\"\n export no_proxy=$NO_PROXY\nelse\n  echo \"Unsetting proxy environment varaibles\"\n  unset PROXY\n  unset proxy\n  unset HTTP_PROXY\n  unset http_proxy\n  unset HTTPS_PROXY\n  unset https_proxy\n  unset NO_PROXY\n  unset no_proxy\n  unset ALL_PROXY\nfi\n\nexport CA_CERTIFICATES=http://employeeshare.mitre.org/m/mjwalsh/transfer/MITRE%20BA%20ROOT.crt,http://employeeshare.mitre.org/m/mjwalsh/transfer/MITRE%20BA%20NPE%20CA-3%281%29.crt\necho \"Setting CA_CERTIFICATES environment variable to $CA_CERTIFICATES\"\n\nexport VAGRANT_ALLOW_PLUGIN_SOURCE_ERRORS=0\necho \"Setting VAGRANT_ALLOW_PLUGIN_SOURCE_ERRORS to $VAGRANT_ALLOW_PLUGIN_SOURCE_ERRORS\"\n\n# Force the use of the vagrant cacert.pem file\necho \"unsetting CURL_CA_BUNDLE and SSL_CERT_FILE environment variables\"\nunset CURL_CA_BUNDLE\nunset SSL_CERT_FILE\n```\n\nWhen in the root of the project, the script can be executed in the terminal session via\n\n```bash\n. ./set_env.sh\n```\n\nIf you're doing this class on your MITRE Life cycle running OS X while on the MITRE network you will **NOT** want to set any proxy related environmental variable. So, you will want to execute this script in this manner\n\n```bash\n. ./set_env.sh no_proxy\n```\n\nIf you have no HTTP proxy and no SSL inspection to be concerned about (such as running the class off of MITRE's corporate network), the alternative is to execute `unset.sh` BASH script to unset all these values:\n\n```bash\n#!/usr/bin/env bash\n\n# Copyright (C) 2019 Michael Joseph Walsh - All Rights Reserved\n# You may use, distribute and modify this code under the\n# terms of the the license.\n#\n# You should have received a copy of the license with\n# this file. If not, please email \u003cmjwalsh@nemonik.com\u003e\n\n# run in shell via\n#\n# ```\n# . ./unset.sh\n# ```\n\nunset no_proxy\nunset NO_PROXY\nunset ALL_PROXY\nunset PROXY\nunset proxy\nunset https_proxy\nunset http_proxy\nunset HTTP_PROXY\nunset HTTPS_PROXY\nunset ftp_proxy\nunset FTP_PROXY\nunset ca_certificates\nunset CA_CERTIFICATES\n```\n\nExecute in terminal session via\n\n```bash\n. ./unset.sh\n```\n\n**On Windows**\n\nIf you are on Windows perform the following to set environmental variable adjusting for your environment:\n\n1. In the Windows taskbar, enter `edit the system environment variables` into `Search Windows` and select the icon with the corresponding name.\n3. The `Systems Property` window will likely open in the background, so you will likely need to go find it and bring it forward.\n4. In the `Systems Property`'s `Advanced` tab select `Environment Variables...` button.\n5. In `Environment Variables` windows that opens, under `User variables for...` press `New ...` to open a `New User Variable` window, enter each `Variable Name` and and its respective `Value` for each pair in the table below\n\n| Variable Name                      | Value                                                                                                                                                           |\n|------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| proxy                              | http://gatekeeper.mitre.org:80                                                                                                                                  |\n| http_proxy                         | http://gatekeeper.mitre.org:80                                                                                                                                  |\n| https_proxy                        | http://gatekeeper.mitre.org:80                                                                                                                                  |\n| no_proxy                           | 127.0.0.1,localhost,.mitre.org,.local,192.168.0.9,192.168.0.10,192.168.0.11,192.168.0.206,192.168.0.10,192.168.0.202,192.168.0.203,192.168.0.204,192.168.0.205 |\n| CA_CERTIFICATES                    | http://pki.mitre.org/MITRE%20BA%20Root.crt,http://pki.mitre.org/MITRE%20BA%20NPE%20CA-3%281%29.crt                                                              |\n| VAGRANT_ALLOW_PLUGIN_SOURCE_ERRORS | 0                                                                                                                                                               |\n\nIf you're on MITRE Institute Lab PC you will want to set all of these variables.\n\nIf you're doing this class on your MITRE Life cycle running Windows (I have yet to verify a Windows MITRE Life cycle.) you will likely **NOT** want to set the `proxy`, `http_proxy`, `https_proxy`, `no_proxy` or any proxy related environmental variables. You will only need to set the `CA_CERTIFICATES` and `VAGRANT_ALLOW_PLUGIN_SOURCE_ERRORS` environment variables.\n\n**NOTE**\n\n- The certificate URLs need to be encoded for parentheses to work.\n- On Windows, you may inadvertently cut-and-paste blank space characters (e.g., tabs, spaces) and the subsequent Ansible automation may fail.\n\n## 9.2. VirtualBox\n\nYou will need to install VirtualBox, a general-purpose full virtualizer for x86 hardware.\n\nThe class has been verified to work with VirtualBox 6.1.14.  Newer version may or may not work.\n\n### 9.2.1. Installing VirtualBox\n\nFor the MITRE Institute class when I teach it, it is assumed VirtualBox is installed, but below are the instructions for installing it on Windows 10.\n\n1. Open your browser to \u003chttps://www.virtualbox.org/wiki/Downloads\u003e\n2. Click `Windows hosts` link under `VirtualBox 6.1.14 platform packages`.\n3. Find and click the installer to install.\n\nYou will also need to turn off `Hyper-V`, `Virtual Machine Platform`, `Windows Sandbox` and `Windows Subsystem for Linux` if installed.\n\n1. Click Windows `Start` and then type `turn Windows features on or off` into the search bar.\n2. Select the icon with the corresponding name.\n3. This will open the `Windows Features` page and then unselect the `Hyper-V`, `Virtual Machine Platform`, `Windows Hypervisor Platform`, `Windows Sandbox` and `Windows Subsystem for Linux` checkboxes if enabled and then click `Okay`.\n\nThe same site has the Mac OS X download. The installation is less involved.\n\nIf you're using Linux use your package manager.  For example, to install on Arch Linux one would use `sudo pacman -Syu virtualbox`.\n\n**NOTE**\n\n- On Windows 10, if you are running into a VirtualBox that is partially working -- Generally, just flaking out. -- check to make sure you have turned off all the features listed above.\n\n## 9.3. Git Bash\n\nGit Bash is `git` packaged for Windows with bash (a command-line shell) and a collection of other, separate *NIX utilities, such as, `ssh`, `scp`, `cat`, `find` and others compiled for Windows.\n\n### 9.3.1. Installing Git Bash\n\nIf you are on Windows, you'll need to install `git`.\n\n1. Download from https://git-scm.com/download/win\n2. Click the installer.\n3. Click `next` until you reach the `Configuring the line ending conversions` page select `Checkout as, commit Unix-style line endings`.\n4. Then `next`, `next`, `next`...\n5. Don't open git-bash from the final window as it will not have the environmental variables set.  Go onto step-6.\n6. On the Windows task bar, enter `git` into `Search Windows` then select `Git Bash`.  Use `Git Bash` instead of `Command` or `Powershell`.\n\nOn OS X, `git` can be installed via [Homebrew](https://brew.sh/) or you can install the Git client directly \u003chttps://git-scm.com/download/mac\u003e.\n\n## 9.4. Retrieve the course material\n\nIf you are reading this on paper and have nothing else, you only have a small portion of the class material. You will need to download the class project containing all the automation to spin up a DevOps toolchain and development virtual machines, etc.\n\nIn a shell, for the purposes of the MITRE Institute class, this means in `Git Bash`, clone the project from [https://github.com/nemonik/hands-on-DevOps.git](https://github.com/nemonik/hands-on-DevOps.git) via git like so:\n\n```\ngit -c http.sslVerify=false clone https://github.com/nemonik/hands-on-DevOps.git\n```\n\nOutput will resemble (i.e., will not be precisely the same):\n\n```\nCloning into 'hands-on-DevOps'...\nremote: Enumerating objects: 52, done.\nremote: Counting objects: 100% (52/52), done.\nremote: Compressing objects: 100% (39/39), done.\nremote: Total 5236 (delta 21), reused 28 (delta 11), pack-reused 5184\nReceiving objects: 100% (5236/5236), 75.60 MiB | 13.40 MiB/s, done.\nResolving deltas: 100% (1578/1578), done.\n```\n\n## 9.5. Infrastructure as code (IaC)\n\nThis class uses Infrastructure as code (IaC) to set up the class environment (i.e., two or more virtual machines that will later be referred to as \"vagrants\".) IaC is the process of provisioning, and configuring (i.e., managing) computer systems through code, rather than directly manipulating the systems by hand (i.e., through manual processes).\n\nThis class uses Vagrant and Ansible IaC frameworks and the following sections will unpack each.\n\n### 9.5.1. Hashicorp Packer\n\nThis class uses Packer, a command-line utility that can be used for many [use cases](https://www.terraform.io/intro/use-cases.html), but I primarily use to build EC2 and VirtualBox machine images.  In the case of VirtualBox to create Vagrant box.  In this class, if you select to use the `nemonik/alpine310` base box in the `ConfigurationVars` module defined in `configuration_vars.rb` file at the root of the project, then Terraform will be invoked to create the `nemonik/alpine310` box, and then this image will be used to create the `nemonik/devops_alpine310` box on which all the vagrants will be based.  If you select `ubuntu/bionic64` or `centos/7` these boxes will be retrieved from Hashicorp and then used to build their respective boxes: `nemonik/devops_bionic64` and `nemonik/devops_7` on top of which the class' vagrants are built.\n\n#### 9.5.1.1. Packer document and source\n\nPacker's documetnation can be found at [https://packer.io/docs/](https://packer.io/docs/).\n\nIts canonical (i.e., authoritative) source can be found at [https://github.com/hashicorp/terraform](https://github.com/hashicorp/terraform).\n\n#### 9.5.1.2. Installing Packer\n\nRetrieved the installationation executable for your machine's operating system from here\n\nhttps://releases.hashicorp.com/packer/1.5.1/\n\nIf you're on OS X you want [https://releases.hashicorp.com/packer/1.5.1/packer_1.5.1_darwin_amd64.zip](https://releases.hashicorp.com/packer/1.5.1/packer_1.5.1_darwin_amd64.zip) and if you're on Windows you want [https://releases.hashicorp.com/packer/1.5.1/packer_1.5.1_windows_amd64.zip](https://releases.hashicorp.com/packer/1.5.1/packer_1.5.1_windows_amd64.zip).\n\nYou may also be able to install Packer via your Linux operating system's package manager.\n\n**Installing on OS X**\n\nTo install on OS X simply unpack and copy to `/user/local/bin/packer` as `root` for example\n\n```bash\ncd ~/Downloads\nunzip ~/Downloads/packer_1.5.1_darwin_amd64.zip\nArchive:  /Users/mjwalsh/Downloads/packer_1.5.1_darwin_amd64.zip\n  inflating: packer\nsudo cp ~/Downloads/packer /usr/local/bin\npacker version\nPacker v1.5.1\n```\n\n**Installing on Windows**\n\nTo install on Windows:\n\n1. Right click the downloaded zip file.\n2. Extract all.\n3. Enter `C:\\Program Files\\packer_1.5.1_windows_amd64` as the path.\n4. Extract.\n5. Continue (Give admin permission.)\n6. In the Windows taskbar, enter `env` into `Search Windows` and select `edit the system environment variables`.\n7. In the `Systems Property`'s `Advanced` tab select `Environment Variables...` button.\n8. In `Environment Variables` windows that opens, in `User variables for...` select the Path variable then select`Edit ...` to open a `Edit environment variable` window.\n9. Select `new` and enter `C:\\Program Files\\packer_1.5.1_windows_amd64`.\n\n#### 9.5.1.3. Packer project explained\n\nThe terraform project to build `nemonik/alpine310` box is found in `box/packer-alpine310` looks like so:\n\n\n![Packer project for Alpine 310](diagrams/packer-alpine310.svg)\n[PlantUML source for this diagram](plantuml/packer-alpine310.puml)\n\n\nThe folders and files have the following purpose:\n- `alpine310.json` - is the packer template used to orchestrate the creation of the `nemonik_alpine310.box`. It defines the number of CPUs, the memory, the ISO used to install the VM's OS, the root password, the automation to install the OS, the scripts to call to further configure, and the box to result\n- `build_box.sh` - is a bash shell script used to run the Packer build\n- `configs` - a folder, holds the Vagrantfile template used in post processing the VM\n- `vagrantfile.tpl` - is the template used in post processing the VM\n- `http` - a folder, holds the `answer` file `wget` into the VM\n- `answer`- holds all the answers provide to `setup-alpine` to configure the VM\n- `isos` - a folder,is a cache that holds the ISOs downloaded to install Alpine, so that they don't have to be repeatedly downloaded\n- `nemonik_alpine310.box` - the vagrant box created by Packer.\n- `packer_cache` - a folder, is a packer cache used to speed production of the box\n- `remove_box.sh` - a bash script used to remove the `nemonik_alpine310.box` from Vagrant\n- `configure` - a folder, holding the shells script used to configure the VM\n- `configure.sh` - the script to configure the VM past what is directed in `builders`' `boot_command` in `alpine310.json`\n\nThe bulk of the work is in `alpine310.json`, `http/answer` and `scripts/configure.sh`.\n\nThe contents of the `alpine310.json`:\n\n```\n{\n  \"description\": \"Build Alpine 3.10 x86_64 vagrant box\",\n  \"variables\": {\n    \"vm_name\": \"alpine-3.10.0-x86_64\",\n    \"cpus\": \"1\",\n    \"memory\": \"1024\",\n    \"disk_size\": \"61440\",\n    \"iso_local_url\": \"isos/x86_64/alpine-virt-3.10.0-x86_64.iso\",\n    \"iso_download_url\": \"http://dl-cdn.alpinelinux.org/alpine/v3.10/releases/x86_64/alpine-virt-3.10.0-x86_64.iso\",\n    \"iso_checksum\": \"b3d8fe65c2777edcbc30b52cde7f5ae21dff8ecda612d5fe7b10d5c23cda40c4\",\n    \"iso_checksum_type\": \"sha256\",\n    \"root_password\": \"vagrant\",\n    \"ssh_username\": \"root\",\n    \"ssh_password\": \"vagrant\"\n  },\n  \"provisioners\": [\n    {\n      \"type\": \"shell\",\n      \"execute_command\": \"/bin/sh -ux '{{.Path}}'\",\n      \"script\": \"scripts/configure.sh\"\n    }\n  ],\n  \"builders\": [\n    {\n      \"type\": \"virtualbox-iso\",\n      \"headless\": false,\n      \"vm_name\": \"{{user `vm_name`}}\",\n      \"format\": \"ova\",\n      \"guest_os_type\": \"Linux26_64\",\n      \"guest_additions_mode\": \"disable\",\n      \"disk_size\": \"{{user `disk_size`}}\",\n      \"iso_urls\": [\n        \"{{user `iso_local_url`}}\",\n        \"{{user `iso_download_url`}}\"\n      ],\n      \"iso_checksum\": \"{{user `iso_checksum`}}\",\n      \"iso_checksum_type\": \"{{user `iso_checksum_type`}}\",\n      \"http_directory\": \"http\",\n      \"communicator\": \"ssh\",\n      \"ssh_username\": \"{{user `ssh_username`}}\",\n      \"ssh_password\": \"{{user `ssh_password`}}\",\n      \"ssh_wait_timeout\": \"10m\",\n      \"shutdown_command\": \"/sbin/poweroff\",\n      \"boot_wait\": \"30s\",\n      \"boot_command\": [\n        \"root\u003center\u003e\u003cwait\u003e\",\n        \"ifconfig eth0 up \u0026\u0026 udhcpc -i eth0\u003center\u003e\u003cwait10\u003e\",\n        \"wget http://{{ .HTTPIP }}:{{ .HTTPPort }}/answers\u003center\u003e\u003cwait\u003e\",\n        \"setup-alpine -f $PWD/answers\u003center\u003e\u003cwait5\u003e\",\n        \"{{user `root_password`}}\u003center\u003e\u003cwait\u003e\",\n        \"{{user `root_password`}}\u003center\u003e\u003cwait\u003e\",\n        \"\u003cwait10\u003e\u003cwait10\u003e\u003cwait10\u003e\",\n        \"\u003cwait10\u003e\u003cwait10\u003e\u003cwait10\u003e\",\n        \"\u003cwait10\u003e\u003cwait10\u003e\u003cwait10\u003e\",\n        \"\u003cwait10\u003e\u003cwait10\u003e\u003cwait10\u003e\",\n        \"y\u003center\u003e\",\n        \"\u003cwait10\u003e\u003cwait10\u003e\u003cwait10\u003e\",\n        \"\u003cwait10\u003e\u003cwait10\u003e\u003cwait10\u003e\",\n        \"\u003cwait10\u003e\u003cwait10\u003e\u003cwait10\u003e\",\n        \"\u003cwait10\u003e\u003cwait10\u003e\u003cwait10\u003e\",\n        \"mount /dev/sda2 /mnt\u003center\u003e\",\n        \"echo 'PermitRootLogin yes' \u003e\u003e /mnt/etc/ssh/sshd_config\u003center\u003e\",\n        \"umount /dev/sda2\u003center\u003e\",\n        \"sync\u003center\u003e\",\n        \"reboot\u003center\u003e\"\n      ],\n      \"hard_drive_interface\": \"sata\",\n      \"vboxmanage\": [\n        [\n          \"modifyvm\",\n          \"{{.Name}}\",\n          \"--memory\",\n          \"{{user `memory`}}\"\n        ],\n        [\n          \"modifyvm\",\n          \"{{.Name}}\",\n          \"--cpus\",\n          \"{{user `cpus`}}\"\n        ],\n        [\n          \"modifyvm\",\n          \"{{.Name}}\",\n          \"--rtcuseutc\",\n          \"on\"\n        ]\n      ]\n    }\n  ],\n  \"post-processors\": [\n    [\n      {\n        \"type\": \"vagrant\",\n        \"vagrantfile_template\": \"configs/vagrantfile.tpl\",\n        \"output\": \"nemonik_alpine310.box\"\n      }\n    ]\n  ]\n}\n```\n\nPacker's `builders`' `boot_command`'s execution is very time sensitive, so you may need to add additional `\u003cwait10\u003e`s if the build was to fail for you.\n\n#### 9.5.1.4. Packer execution\n\nAlong the way a modal will pop up asking you \"Do you want the application “packer” to accept incoming network connections?\". Give your approval otherwise Packer won't be able to do its job. The command-line output of Packer when creating the nemonik_alpine310.box will resemble:\n\n```packer\nINFO: Creating nemonik/devops_alpine310 box...\nsetting PROXYOPTS to none in http/answers\nbuilding nemonik_alpine310.box via packer\nvirtualbox-iso: output will be in this color.\n\n==\u003e virtualbox-iso: Retrieving ISO\n==\u003e virtualbox-iso: Trying isos/x86_64/alpine-virt-3.10.0-x86_64.iso\n==\u003e virtualbox-iso: Trying isos/x86_64/alpine-virt-3.10.0-x86_64.iso?checksum=sha256%3Ab3d8fe65c2777edcbc30b52cde7f5ae21dff8ecda612d5fe7b10d5c23cda40c4\n==\u003e virtualbox-iso: isos/x86_64/alpine-virt-3.10.0-x86_64.iso?checksum=sha256%3Ab3d8fe65c2777edcbc30b52cde7f5ae21dff8ecda612d5fe7b10d5c23cda40c4 =\u003e /Users/mjwalsh/Development/workspace/DevOps_class/alpine-playground/box/packer-alpine310/packer_cache/0172be7009f3c62b985d67dec5495188c7596bcd.iso\n==\u003e virtualbox-iso: Starting HTTP server on port 8922\n==\u003e virtualbox-iso: Creating virtual machine...\n==\u003e virtualbox-iso: Creating hard drive...\n==\u003e virtualbox-iso: Creating forwarded port mapping for communicator (SSH, WinRM, etc) (host port 3684)\n==\u003e virtualbox-iso: Executing custom VBoxManage commands...\n    virtualbox-iso: Executing: modifyvm alpine-3.10.0-x86_64 --memory 1024\n    virtualbox-iso: Executing: modifyvm alpine-3.10.0-x86_64 --cpus 1\n    virtualbox-iso: Executing: modifyvm alpine-3.10.0-x86_64 --rtcuseutc on\n==\u003e virtualbox-iso: Starting the virtual machine...\n==\u003e virtualbox-iso: Waiting 30s for boot...\n==\u003e virtualbox-iso: Typing the boot command...\n```\n\nA little VirtualBox window for the VM will also pop up, so that you can monitor progress as Alpine is installed. At some point Alpine will reboot and Packer will continue outputting:\n\n```packer\n==\u003e virtualbox-iso: Using ssh communicator to connect: 127.0.0.1\n==\u003e virtualbox-iso: Waiting for SSH to become available...\n==\u003e virtualbox-iso: Provisioning with shell script: scripts/configure.sh\n==\u003e virtualbox-iso: + rc-update -u\n    virtualbox-iso:  * Caching service dependencies ... [ ok ]\n==\u003e virtualbox-iso: + setup-apkcache\n==\u003e virtualbox-iso: + apk update\n    virtualbox-iso: Enter apk cache directory (or '?' or 'none') [/var/cache/apk]: fetch http://dl-cdn.alpinelinux.org/alpine/v3.10/main/x86_64/APKINDEX.tar.gz\n    virtualbox-iso: v3.10.4-14-g975b6a3945 [http://dl-cdn.alpinelinux.org/alpine/v3.10/main/]\n    virtualbox-iso: OK: 5669 distinct packages available\n==\u003e virtualbox-iso: + apk upgrade -U --available\n    virtualbox-iso: OK: 87 MiB in 52 packages\n==\u003e virtualbox-iso: + apk add -U bash bash-completion sudo curl\n    virtualbox-iso: (1/10) Installing readline (8.0.0-r0)\n    virtualbox-iso: (2/10) Installing bash (5.0.0-r0)\n    virtualbox-iso: Executing bash-5.0.0-r0.post-install\n    virtualbox-iso: (3/10) Installing bash-completion (2.8-r0)\n    virtualbox-iso: (4/10) Installing openrc-bash-completion (0.41.2-r1)\n    virtualbox-iso: (5/10) Installing ca-certificates (20190108-r0)\n    virtualbox-iso: (6/10) Installing nghttp2-libs (1.39.2-r0)\n    virtualbox-iso: (7/10) Installing libcurl (7.66.0-r0)\n    virtualbox-iso: (8/10) Installing curl (7.66.0-r0)\n    virtualbox-iso: (9/10) Installing kmod-bash-completion (24-r1)\n    virtualbox-iso: (10/10) Installing sudo (1.8.27-r2)\n    virtualbox-iso: Executing busybox-1.30.1-r3.trigger\n    virtualbox-iso: Executing ca-certificates-20190108-r0.trigger\n    virtualbox-iso: OK: 94 MiB in 62 packages\n==\u003e virtualbox-iso: + echo http://dl-cdn.alpinelinux.org/alpine/v3.10/community\n==\u003e virtualbox-iso: + apk add -U virtualbox-guest-additions virtualbox-guest-modules-virt\n    virtualbox-iso: fetch http://dl-cdn.alpinelinux.org/alpine/v3.10/community/x86_64/APKINDEX.tar.gz\n    virtualbox-iso: (1/3) Installing virtualbox-guest-additions (6.0.8-r1)\n    virtualbox-iso: Executing virtualbox-guest-additions-6.0.8-r1.pre-install\n    virtualbox-iso: (2/3) Installing virtualbox-guest-modules-virt (4.19.98-r0)\n    virtualbox-iso: (3/3) Installing virtualbox-guest-additions-openrc (6.0.8-r1)\n    virtualbox-iso: Executing busybox-1.30.1-r3.trigger\n    virtualbox-iso: Executing kmod-24-r1.trigger\n    virtualbox-iso: OK: 98 MiB in 65 packages\n==\u003e virtualbox-iso: + rc-update add virtualbox-guest-additions\n    virtualbox-iso:  * service virtualbox-guest-additions added to runlevel default\n==\u003e virtualbox-iso: + echo vboxsf\n==\u003e virtualbox-iso: + echo 'UseDNS no'\n==\u003e virtualbox-iso: + adduser -D vagrant\n==\u003e virtualbox-iso: + chpasswd\n==\u003e virtualbox-iso: + echo vagrant:vagrant\n==\u003e virtualbox-iso: chpasswd: password for 'vagrant' changed\n==\u003e virtualbox-iso: + mkdir -pm 700 /home/vagrant/.ssh\n==\u003e virtualbox-iso: + chown -R vagrant:vagrant /home/vagrant/.ssh\n==\u003e virtualbox-iso: + wget -O /home/vagrant/.ssh/authorized_keys https://raw.githubusercontent.com/mitchellh/vagrant/master/keys/vagrant.pub\n==\u003e virtualbox-iso: Connecting to raw.githubusercontent.com (151.101.0.133:443)\n==\u003e virtualbox-iso: authorized_keys      100% |********************************|   409  0:00:00 ETA\n==\u003e virtualbox-iso: + chmod 644 /home/vagrant/.ssh/authorized_keys\n==\u003e virtualbox-iso: + adduser vagrant wheel\n==\u003e virtualbox-iso: + echo 'Defaults exempt_group=wheel'\n==\u003e virtualbox-iso: + echo '%wheel ALL=NOPASSWD:ALL'\n==\u003e virtualbox-iso: + sed -i.bak 's@/ash$@/bash@g' /etc/passwd\n==\u003e virtualbox-iso: + rm /etc/passwd.bak\n==\u003e virtualbox-iso: + echo\n==\u003e virtualbox-iso: + rm -Rf /var/cache/apk/APKINDEX.6456cd1a.tar.gz /var/cache/apk/APKINDEX.d8b2a6f4.tar.gz /var/cache/apk/bash-5.0.0-r0.045599ea.apk /var/cache/apk/bash-completion-2.8-r0.c0f500d3.apk /var/cache/apk/ca-certificates-20190108-r0.c0ec382f.apk /var/cache/apk/curl-7.66.0-r0.86434890.apk /var/cache/apk/installed /var/cache/apk/kmod-bash-completion-24-r1.ea84cd3e.apk /var/cache/apk/libcurl-7.66.0-r0.ae6524b8.apk /var/cache/apk/nghttp2-libs-1.39.2-r0.6f35d6f8.apk /var/cache/apk/openrc-bash-completion-0.41.2-r1.198430a1.apk /var/cache/apk/readline-8.0.0-r0.32186548.apk /var/cache/apk/sudo-1.8.27-r2.fe94d45e.apk /var/cache/apk/virtualbox-guest-additions-6.0.8-r1.b025c3f0.apk /var/cache/apk/virtualbox-guest-additions-openrc-6.0.8-r1.b528fb1a.apk /var/cache/apk/virtualbox-guest-modules-virt-4.19.98-r0.f198f848.apk\n==\u003e virtualbox-iso: + dd 'if=/dev/zero' 'of=/EMPTY' 'bs=1M'\n```\n\nIt will take some time to zero out the unused portion of the root drive, but Packer will continue:\n\n```packer\n==\u003e virtualbox-iso: 59959+0 records in\n==\u003e virtualbox-iso: 59958+0 records out\n==\u003e virtualbox-iso: + rm -f /EMPTY\n==\u003e virtualbox-iso: + cat /dev/null\n==\u003e virtualbox-iso: + history -c\n==\u003e virtualbox-iso: + sync\n==\u003e virtualbox-iso: + sync\n==\u003e virtualbox-iso: + sync\n==\u003e virtualbox-iso: + exit 0\n==\u003e virtualbox-iso: Gracefully halting virtual machine...\n==\u003e virtualbox-iso: Preparing to export machine...\n    virtualbox-iso: Deleting forwarded port mapping for the communicator (SSH, WinRM, etc) (host port 3684)\n==\u003e virtualbox-iso: Exporting virtual machine...\n    virtualbox-iso: Executing: export alpine-3.10.0-x86_64 --output output-virtualbox-iso/alpine-3.10.0-x86_64.ova\n==\u003e virtualbox-iso: Deregistering and deleting VM...\n==\u003e virtualbox-iso: Running post-processor: vagrant\n==\u003e virtualbox-iso (vagrant): Creating Vagrant box for 'virtualbox' provider\n    virtualbox-iso (vagrant): Unpacking OVA: output-virtualbox-iso/alpine-3.10.0-x86_64.ova\n    virtualbox-iso (vagrant): Renaming the OVF to box.ovf...\n    virtualbox-iso (vagrant): Using custom Vagrantfile: configs/vagrantfile.tpl\n    virtualbox-iso (vagrant): Compressing: Vagrantfile\n    virtualbox-iso (vagrant): Compressing: alpine-3.10.0-x86_64-disk001.vmdk\n    virtualbox-iso (vagrant): Compressing: box.ovf\n    virtualbox-iso (vagrant): Compressing: metadata.json\nBuild 'virtualbox-iso' finished.\n\n==\u003e Builds finished. The artifacts of successful builds are:\n--\u003e virtualbox-iso: 'virtualbox' provider box: nemonik_alpine310.box\n```\n\n### 9.5.2. Vagrant\n\nThis class uses Vagrant, a command-line utility for managing the life cycle of virtual machines as a vagrant in that the VMs are not meant to hang around in the same place for very long.\n\nUnless you want to pollute your machine with every imaginable programming language, framework and library version you'll find yourself often creating a virtual machine (VM) for each software project. Sometimes more than one. And if you're like me of the past you'll end up with a VirtualBox full of VMs. If you haven't gone about this the right way, you'll end up wondering what VM went with which project and now how did I create it? The anti-pattern around this problem is to write documentation. A better way that aligns with a DevOps repeatable practices is to create automation to provision and configure your development VMs. This is where Vagrant comes in as it is \"a command-line utility for managing the life cycle of virtual machines.\"\n\n#### 9.5.2.1. Vagrant documentation and source\n\nVagrant's documentation can be found at\n\n\u003chttps://www.vagrantup.com/docs/index.html\u003e\n\nIt's canonical (i.e., authoritative) source can be found at\n\n\u003chttps://github.com/hashicorp/vagrant/\u003e\n\nVagrant is written in Ruby. In fact, a Vagrantfile is written in a Ruby DSL and I make full use of this to extend the functionality of the Vagrantfile..\n\n#### 9.5.2.2. Installing Vagrant\n\n1. If you are Windows or OS X download Vagrant from\n\n  \u003chttps://releases.hashicorp.com/vagrant/2.2.10/\u003e\n\n  The class has been verified to work with Version 2.2.10.  Newer version may or may not work.\n\n  If you're using Linux, use your operating system's package manager to install `vagrant`.  For exmple, to install on Arch Linux one would use\n\n  ```bash\n  sudo pacman -Syu vagrant\n  ```\n\n2. Click on the installer once downloaded and follow along. On Windows, the installer may stall calculating for a bit and may bury modals you'll need to respond to in the Windows Task bar, so keep an eye out for that. The installer will automatically add the vagrant command to your system path so that it is available on the command line. If it is not found, the documentation advises you to try logging out and logging back into your system. This is particularly necessary sometimes for Windows. Windows will require a reboot, so remember to **come back and complete step-3, if you are on the MITRE corporate network**.\n\n3. **If you're not on the MITRE corporate network please skip this step.**\n\n   - On Windows, use the File Explorer to replace the existing `C:\\Hashicorp\\vagrant\\embedded\\cacert.pem` file with the project's `vagrant_files/cacert.pem` by using the File Explorer.\n\n   - On Mac OS X, copy it to `/opt/vagrant/embedded` as *root* using\n\n   ```bash\n   sudo cp vagrant_files/cacert.pem /opt/vagrant/embedded/.\n   ```\n\n**NOTE**\n\n- Vagrant respects `SSL_CERT_FILE` and `CURL_CA_BUNDLE` environment variables used to point to cacert bundles.  If you run into SSL errors, you may have `SSL_CERT_FILE` and/or `CURL_CA_BUNDLE` environment variable files set requiring you to add MITRE CA certificates to the file specified by these environment variables.  If you use the `set_env.sh` at the root of the project it will unset these environment variables forcing vagrant to use its cacert.pem file you replace above.\n- The same site has the Mac OS X download, whose installation is less involved.\n\n#### 9.5.2.3. The Vagrantfile explained\n\nThe `Vagrantfile` found at the root of the project describes how to provision and configure one or more virtual machines.\n\nVagrant's own documentation puts it best:\n\n\u003e Vagrant is meant to run with one Vagrantfile per project, and the Vagrantfile is\n\u003e supposed to be committed to version control. This allows other developers involved\n\u003e in the project to check out the code, run vagrant up, and be on their way.\n\u003e Vagrantfiles are portable across every platform Vagrant supports.\n\nIf we were instead provisioning Amazon EC2 instances, we'd alternatively use [Terraform](https://www.terraform.io/), a tool for building, changing, and versioning infrastructure.\n\nThe following sub-sections enumerate the various sections of the `Vagrantfile` broken apart in order to discuss.\n\n##### 9.5.2.3.1. Modelines\n\n```ruby\n# -*- mode: ruby -*-\n# vi: set ft=ruby :\n```\n\nWhen authoring, tells your text editor (e.g. emacs or vim) to choose a specific editing mode for the Vagrantfile. Line one is a [modeline for emacs](http://www.gnu.org/software/emacs/manual/html_node/emacs/Choosing-Modes.html) and line two is a [modeline for vim](http://vim.wikia.com/wiki/Modeline_magic).\n\n##### 9.5.2.3.2. Setting extra variables for Ansible roles\n\nThe following lines:\n\n```ruby\n# Vagrant will start at your current path and then move upward looking\n# for a Vagrant file.  The following will provide the path for the found\n# Vagrantfile, so you can execute `vagrant` commands on the command-line\n# anywhere in the project a Vagrantfile doesn't already exist.\nvagrantfilePath = \"\"\nif File.dirname(__FILE__).end_with?('Vagrantfile')\n   vagrantfilePath = File.dirname(File.dirname(__FILE__))\nelse\n   vagrantfilePath = File.dirname(__FILE__)\nend\n\n# Used to hold all the configuration variable and convienance methods for accessing\nrequire File.join(vagrantfilePath, 'configuration_vars.rb')\n```\n\nin the Vagrantfile at the root of the project will include `configuration_vars.rb` Ruby module.  This Ruby module defines the Ansible extra_vars used by Ansible roles found in `ansible/roles` and `box/ansible/role` as well as the bash scripting templates used by the `Vagrantfile` at the root of the project as well as the `box/Vagrantfile` as well as the bash scripts found in `box/`.  You would update the version of GitLab used by the class by updating `:gitlab_version` here.\n\nThe contents of `configuration_vars.rb` Ruby module will resemble:\n\n```ruby\n#-*- mode: ruby -*-\n# vi: set ft=ruby :\n\n# Copyright (C) 2020 Michael Joseph Walsh - All Rights Reserved\n# You may use, distribute and modify this code under the\n# terms of the the license.\n#\n# You should have received a copy of the license with\n# this file. If not, please email \u003cmjwalsh@nemonik.com\u003e\n\nmodule ConfigurationVars\n\n  # Define variablese\n  VARS = {\n\n    # The network block the cluster and apps will be in\n    network_prefix: \"192.168.0\",\n\n#    # Add OpenEBS drives ('yes'/'no')\n#    openebs_drives: 'yes',\n    openebs_drives: 'no',\n\n    # Sets the OpenEBS drive size in GB\n    openebs_drive_size_in_gb: 100,\n\n    # Provision and configure development vagrant ('yes'/'no')\n#    create_development: 'no',\n    create_development: 'yes',\n#    development_is_worker_node: 'yes',\n\n    # The number of nodes to provision the Kubernetes cluster. One will be a master.\n    nodes: 2,\n#    nodes: 1,\n\n    # The Vagrant box to base our DevOps box on.  Pick just one.\n\n    base_box: 'centos/7',\n    base_box_version: '2004.01',\n\n#    base_box: 'ubuntu/bionic64',\n#    base_box_version: '20200304.0.0',\n\n#    base_box: 'nemonik/alpine310',\n#    base_box_version: '0',\n\n    vagrant_root_drive_size: '80GB',\n\n    ansible_version: '2.10.3',\n\n    default_retries: '60',\n    default_delay: '10',\n\n    docker_timeout: '300',\n    docker_retries: '60',\n    docker_delay: '10',\n\n    k3s_version: 'v1.19.4+k3s1',\n    k3s_cluster_secret: 'kluster_secret',\n\n    kubectl_version: 'v1.19.4',\n    kubectl_checksum: 'sha256:7df333f1fc1207d600139fe8196688303d05fbbc6836577808cda8fe1e3ea63f',\n\n    kubernetes_dashboard: 'yes',\n    kubernetes_dashboard_version: 'v2.0.0',\n\n    traefik: 'yes',\n    traefik_version: '1.7.26',\n    traefik_http_port: '80',\n    traefik_admin_port: '8080',\n    traefik_host: '192.168.0.206',\n\n    metallb: 'yes',\n    metallb_version: 'v0.9.5',\n\n    kompose_version: '1.18.0',\n\n    docker_compose_version: '1.27.4',\n    docker_compose_pip_version: '1.25.0rc2',\n\n    helm_cli_version: '3.2.1',\n    helm_cli_checksum: '018f9908cb950701a5d59e757653a790c66d8eda288625dbb185354ca6f41f6b',\n\n    registry_version: '2.7.1',\n    registry: 'yes',\n    registry_host: '192.168.0.10',\n    registry_port: '5000',\n    passthrough_registry: 'yes',\n    passthrough_registry_host: '192.168.0.10',\n    passthrough_registry_port: '5001',\n    registry_deploy_via: 'docker-compose',\n\n    gitlab: 'yes',\n    gitlab_version: '13.2.3',\n    gitlab_host: '192.168.0.202',\n    gitlab_port: '80',\n    gitlab_ssh_port: '10022',\n    gitlab_user: 'root',\n\n    drone: 'yes',\n    drone_version: '1.9.0',\n    drone_runner_docker_version: '1.4.0',\n    drone_host: '192.168.0.10',\n\n    drone_cli_version: 'v1.2.1',\n\n    plantuml_server: 'yes',\n    plantuml_server_version: 'latest',\n    plantuml_host: '192.168.0.203',\n    plantuml_port: '80',\n\n    taiga: 'yes',\n    taiga_version: 'latest',\n    taiga_host: '192.168.0.204',\n    taiga_port: '80',\n\n    sonarqube: 'yes',\n    sonarqube_version: '8.5.1-community',\n    sonarqube_host: '192.168.0.205',\n    sonarqube_port: '9000',\n\n    sonar_scanner_cli_version: '4.3.0.2102',\n\n    inspec_version: '4.18.39',\n\n    python_container_image: 'yes',\n    python_version: '2.7.18',\n\n    golang_container_image: 'yes',\n    golang_sonarqube_scanner_image: 'yes',\n    golang_version: '1.15',\n\n    selenium_standalone_chrome_version: '3.141',\n\n    standalone_firefox_container_image: 'yes',\n    selenium_standalone_firefox_version: '3.141',\n\n    owasp_zap2docker_stable_image: 'yes',\n    zap2docker_stable_version: '2.8.0',\n\n    openwhisk: 'yes',\n    openwhisk_host: '192.168.0.207',\n\n    cache_path: '/vagrant/cache',\n    images_cache_path: '/vagrant/cache/images',\n\n    create_cache: 'yes',\n\n    host_os: (\n      host_os = RbConfig::CONFIG['host_os']\n      case host_os\n      when /mswin|msys|mingw|cygwin|bccwin|wince|emc/\n        'windows'\n      when /darwin|mac os/\n        'macosx'\n      when /linux/\n        'linux'\n      when /bsd/\n        'unix'\n      else\n        raise Error, \"unknown os: #{host_os.inspect}\"\n      end\n    )\n  }\n\n  VARS[:ansible_python_version] = (\n   if VARS[:base_box].downcase.include? 'centos' and VARS[:base_box].to_s.include? '7'\n     'python2'\n   else\n     'python3'\n   end\n  )\n\n  def ConfigurationVars.as_string( http_proxy, https_proxy, ftp_proxy, no_proxy, certs)\n\n    vars = VARS\n\n    vars[:http_proxy] = (!http_proxy ? \"\" : http_proxy)\n    vars[:https_proxy] = (!https_proxy ? \"\" : https_proxy)\n    vars[:ftp_proxy] = (!ftp_proxy ? \"\" : ftp_proxy)\n    vars[:no_proxy] = (!no_proxy ? \"\" : no_proxy)\n\n    vars[:CA_CERTIFICATES] = ''\n\n    unless certs.nil? || certs == ''\n      vars[:CA_CERTIFICATES] = certs\n    end\n\n    vars_string = ''\n\n    vars.each do |key, value|\n      if ( ( key == :CA_CERTIFICATES ) \u0026\u0026 ( !value.nil? ) \u0026\u0026 value != '' )\n        vars_string = vars_string + \"\\\\\\\"#{key}\\\\\\\":\\\\\\[\"\n        value.each { |item|\n          vars_string = vars_string + \"\\\\\\\"#{item}\\\\\\\",\"\n        }\n        vars_string = vars_string.chop + '\\\\],'\n      else\n        if (value.is_a? Integer)\n          value = value.to_s\n        end\n\n        vars_string = vars_string + \"\\\\\\\"#{key}\\\\\\\":\\\\\\\"#{value}\\\\\\\",\"\n      end\n    end\n\n    return '\\\\{' + vars_string.chop + '\\\\}'\n  end\n\n  DETERMINE_OS_TEMPLATE = \u003c\u003c~SHELL\n    echo Determining OS...\n\n    os=\"\"\n    if [[ $(command -v lsb_release | wc -l) == *\"1\"* ]]; then\n      os=\"$(lsb_release -is)-$(lsb_release -cs)\"\n    elif [ -f \"/etc/os-release\" ]; then\n      if [[ $(cat /etc/os-release | grep -i alpine | wc -l) -gt \"0\" ]]; then\n        os=\"Alpine\"\n      elif [[ $(cat /etc/os-release | grep -i \"CentOS Linux 7\" | wc -l) -gt \"0\" ]]; then\n        os=\"CentOS 7\"\n      fi\n    else\n      echo -n \"Cannot determine OS.\"\n      exit -1\n    fi\n  SHELL\n\n  OS_PACKAGES_FROM_CACHE_TEMPLATE = \u003c\u003c~SHELL\n\n    echo OS packages from cache...\n\n    mkdir -p /tmp/root-cache\n\n    box=\"#{VARS[:base_box]}\"\n\n    case $os in\n\n      \"Alpine\")\n        package_manager=\"apk\"\n        ;;\n\n      \"Ubuntu-bionic\")\n        package_manager=\"apt\"\n        ;;\n\n      \"CentOS 7\")\n        package_manager=\"yum\"\n        ;;\n\n      *)\n        echo -n \"${os} not supported.\"\n        exit -1\n        ;;\n    esac\n\n    if [ -f \"/vagrant/cache/TYPE/${box}/${package_manager}.tar.gz\" ]; then\n      update=true\n\n      if [ -f \"/tmp/root-cache/${package_manager}.tar.gz\" ]; then\n        if ((`stat -c%s \"/vagrant/cache/TYPE/${box}/${package_manager}.tar.gz\"`!=`stat -c%s \"/tmp/root-cache/${package_manager}.tar.gz\"`)); then\n          update=false\n        fi\n      fi\n\n      if ($update == true); then\n        echo Installing ${box} ${package_manager} packages from cache...\n        cd /tmp/root-cache\n        cp /vagrant/cache/TYPE/${box}/${package_manager}.tar.gz ${package_manager}.tar.gz\n        tar zxf ${package_manager}.tar.gz\n        if [ -d \"${package_manager}\" ]; then\n          case $os in\n            \"Alpine\")\n              echo \"installing apk packages from cache...\"\n              apk add --repositories-file=/dev/null --allow-untrusted --no-network apk/*.apk\n              ;;\n            \"Ubuntu-bionic\")\n              #echo \"installing apt packages from cache...\"\n              #cd apt/archives\n              #dpkg -i ./*.deb\n              #apt --fix-broken install\n              echo \"not yet reliable...\"\n              ;;\n            \"CentOS 7\")\n              mv yum /var/cache/\n              ;;\n            *)\n              echo \"${os} not supported.\" 1\u003e\u00262\n              exit -1\n              ;;\n          esac\n        fi\n\n        rm -Rf ${package_manager}\n      else\n        echo No new ${box} packages in cache...\n      fi\n    else\n      echo No cached ${box} packages...\n    fi\n  SHELL\n\n  ROOT_INSTALL_ANSIBLE_DEPENDENCIES_TEMPLATE = \u003c\u003c~SHELL\n    echo Root installing ansible dependencies...\n\n    case $os in\n      \"Alpine\")\n        # install Alpine packages\n        apk add python3 python3-dev py3-pip musl-dev libffi-dev libc-dev py3-cryptography make gcc libressl-dev\n        ;;\n      \"Ubuntu-bionic\")\n        apt update\n        apt upgrade -y\n        apt install -y python3 python3-dev python3-pip make gcc\n        ;;\n      \"CentOS 7\")\n        yum update -y\n        yum install -y epel-release\n        yum install -y python-pip python-devel make gcc python-cffi\n#        pip uninstall -y bcrypt\n#        yum --enablerepo=epel install -y python2-bcrypt\n        ;;\n      *)\n        echo \"${os} not supported.\" 1\u003e\u00262\n        exit -1\n        ;;\n    esac\n  SHELL\n\n  USER_INSTALL_DEPENDENCIES_TEMPLATE = \u003c\u003c~SHELL\n    echo User installing ansible dependencies...\n\n    #{ConfigurationVars::VARS[:ansible_python_version]} -m pip install --user --upgrade pip\n    /home/vagrant/.local/bin/pip install --user --upgrade setuptools\n    /home/vagrant/.local/bin/pip install --user paramiko ansible==#{ConfigurationVars::VARS[:ansible_version]}\n\n    case $os in\n      \"Alpine\"|\"Ubuntu-bionic\")\n        ;;\n      \"CentOS 7\")\n#        /home/vagrant/.local/bin/pip uninstall -y bcrypt\n#        /home/vagrant/.local/bin/pip install -y bcrypt==3.1.7\n        ;;\n      *)\n        echo \"${os} not supported.\" 1\u003e\u00262\n        exit -1\n        ;;\n    esac\n  SHELL\n\n  INSTALL_ANSIBLE_TEMPLATE = \u003c\u003c~SHELL\n    case $os in\n      \"Alpine\"|\"Ubuntu-bionic\"|\"CentOS 7\")\n        #{ConfigurationVars::VARS[:ansible_python_version]} -m pip install --user --upgrade pip setuptools\n        #{ConfigurationVars::VARS[:ansible_python_version]} -m pip install --user paramiko ansible==#{ConfigurationVars::VARS[:ansible_version]}\n        ;;\n      *)\n        echo \"${os} not supported.\" 1\u003e\u00262\n        exit -1\n        ;;\n    esac\n  SHELL\n\n  RUN_ANSIBLE_TEMPLATE = \u003c\u003c~SHELL\n    echo Running ansible-playbook PLAYBOOK_PATH...\n\n    case $os in\n      \"Alpine\"|\"Ubuntu-bionic\"|\"CentOS 7\")\n        n=0\n        until [ \"$n\" -ge #{ConfigurationVars::VARS[:default_retries]} ]; do\n          /home/vagrant/.local/bin/ansible-galaxy install --force --roles-path ansible/roles --role-file requirements.yml \u0026\u0026 break\n          n=$((n+1))\n          sleep #{ConfigurationVars::VARS[:default_delay]}\n        done\n        PYTHONUNBUFFERED=1 ANSIBLE_FORCE_COLOR=true /home/vagrant/.local/bin/ansible-playbook PLAYBOOK_PATH --limit=\"LIMIT\" --extra-vars=ANSIBLE_EXTRA_VARS --extra-vars='ansible_python_interpreter=\"/usr/bin/env #{ConfigurationVars::VARS[:ansible_python_version]}\"' --vault-password-file=VAULT_PASS_PATH -vvvv --connection=local --inventory=INVENTORY_PATH\n        ;;\n#      \"CentOS 7\")\n#        n=0\n#        until [ \"$n\" -ge #{ConfigurationVars::VARS[:default_retries]} ]; do\n#          ansible-galaxy install --force --roles-path ansible/roles --role-file requirements.yml \u0026\u0026 break\n#          n=$((n+1))\n#          sleep #{ConfigurationVars::VARS[:default_delay]}\n#        done\n#        PYTHONUNBUFFERED=1 ANSIBLE_FORCE_COLOR=true ansible-playbook PLAYBOOK_PATH --limit=\"LIMIT\" --extra-vars=ANSIBLE_EXTRA_VARS  --vault-password-file=VAULT_PASS_PATH -vvvv --connection=local --inventory=INVENTORY_PATH\n#        ;;\n      *)\n        echo \"${os} not supported.\" 1\u003e\u00262\n        exit -1\n        ;;\n    esac\n  SHELL\n\n  RESIZE_ROOT_TEMPLATE = \u003c\u003c~SHELL\n    echo Resizing root...\n\n    case $os in\n      \"Alpine\")\n        # install Alpine packages\n        echo \"resize root not yet handled.\"\n        ;;\n      \"Ubuntu-bionic\")\n      echo \"resize root not yet handled.\"\n        ;;\n      \"CentOS 7\")\n      echo \"Resizing root volume...\"\n      yum -y install cloud-utils-growpart\n      growpart /dev/sda 1\n      xfs_growfs /\n        ;;\n      *)\n        echo \"${os} not supported.\" 1\u003e\u00262\n        exit -1\n        ;;\n    esac\n  SHELL\n\n  SITE_PACKAGES_FROM_CACHE_TEMPLATE = \u003c\u003c~SHELL\n\n    echo Site packages from cache...\n\n    if [ -f \"/vagrant/cache/TYPE/${box}/site-packages.tar.gz\" ]; then\n      update=true\n\n      if [ -f \"/tmp/root-cache/site-packages.tar.gz\" ]; then\n        if ((`stat -c%s \"/vagrant/cache/TYPE/${box}/site-packages\"`!=`stat -c%s \"/tmp/root-cache/site-packages\"`)); then\n          update=false\n        fi\n      fi\n\n      if ($update == true); then\n        echo Unpacking #{ VARS[:ansible_python_version] } site-packages from cache...\n        cd /tmp/root-cache\n        cp /vagrant/cache/TYPE/${box}/site-packages.tar.gz site-packages.tar.gz\n        cd /usr/lib/#{ VARS[:ansible_python_version] }*\n        tar zxvf /tmp/root-cache/site-packages.tar.gz\n      else\n        echo No new updates to site-packages in cache...\n      fi\n    fi\n  SHELL\n\n  USER_CACHED_CONTENT_TEMPLATE = \u003c\u003c~SHELL\n    echo Use cached content...\n\n    mkdir -p /tmp/vagrant-cache\n\n    box=\"#{VARS[:base_box]}\"\n\n    if [ \"${os}\" == \"CentOS 7\" ] \u0026\u0026 [ -f \"/vagrant/cache/TYPE/${box}/rvm.tar.gz\" ]; then\n      update=true\n\n      if [ -f \"/tmp/vagrant-cache/rvm.tar.gz\" ]; then\n        if ((`stat -c%s \"/vagrant/cache/TYPE/${box}/rvm.tar.gz\"`!=`stat -c%s \"/tmp/vagrant-cache/rvm.tar.gz\"`)); then\n          update=false\n        fi\n      fi\n\n      if ($update == true); then\n        echo Unpacking /home/vagrant/[.rvm .gnupg .bash_profile .bashrc .profile .mkshrc .zshrc .zlogin] from cache...\n        cp /vagrant/cache/TYPE/${box}/rvm.tar.gz /tmp/vagrant-cache/rvm.tar.gz\n        cd /home/vagrant/\n        tar zxvf /tmp/vagrant-cache/rvm.tar.gz\n      else\n        echo No new updates to /home/vagrant/[.rvm .gnupg .bash_profile .bashrc .profile .mkshrc .zshrc .zlogin] in cache...\n      fi\n    else\n      echo No /home/vagrant/[.rvm .gnupg .bash_profile .bashrc .profile .mkshrc .zshrc .zlogin] in cache...\n    fi\n\n    if [ -f \"/vagrant/cache/TYPE/${box}/cache.tar.gz\" ]; then\n      update=true\n\n      if [ -f \"/tmp/vagrant-cache/cache.tar.gz\" ]; then\n        if ((`stat -c%s \"/vagrant/cache/TYPE/${box}/cache.tar.gz\"`!=`stat -c%s \"/tmp/vagrant-cache/cache.tar.gz\"`)); then\n          update=false\n        fi\n      fi\n\n      if ($update == true); then\n        echo Unpacking /home/vagrant/.cache from cache...\n        cp /vagrant/cache/TYPE/${box}/cache.tar.gz /tmp/vagrant-cache/cache.tar.gz\n        cd /home/vagrant/\n        tar zxvf /tmp/vagrant-cache/cache.tar.gz\n      else\n        echo No new updates to /home/vagrant/.cache in cache...\n      fi\n    else\n      echo No /home/vagrant/.cache cache...\n    fi\n\n    if [ -f \"/vagrant/cache/TYPE/${box}/local.tar.gz\" ]; then\n      update=true\n\n      if [ -f \"/tmp/vagrant-cache/local.tar.gz\" ]; then\n        if ((`stat -c%s \"/vagrant/cache/TYPE/${box}/local.tar.gz\"`!=`stat -c%s \"/tmp/vagrant-cache/local.tar.gz\"`)); then\n          update=false\n        fi\n      fi\n\n      if ($update == true); then\n        echo Unpacking /home/vagrant/.local from cache...\n        cp /vagrant/cache/TYPE/${box}/local.tar.gz /tmp/vagrant-cache/local.tar.gz\n        cd /home/vagrant/\n        tar -zxvf /tmp/vagrant-cache/local.tar.gz\n      else\n        echo No new updates to /home/vagrant/.local in cache...\n      fi\n    else\n      echo No /home/vagrant/.local cache...\n    fi\n\n    if [ -f \"/vagrant/cache/TYPE/${box}/gem.tar.gz\" ]; then\n      update=true\n\n      if [ -f \"/tmp/vagrant-cache/gem.tar.gz\" ]; then\n        if ((`stat -c%s \"/vagrant/cache/TYPE/${box}/gem.tar.gz\"`!=`stat -c%s \"/tmp/vagrant-cache/gem.tar.gz\"`)); then\n          update=false\n        fi\n      fi\n\n      if ($update == true); then\n        echo Unpacking /home/vagrant/.gem from cache...\n        cp /vagrant/cache/TYPE/${box}/gem.tar.gz /tmp/vagrant-cache/gem.tar.gz\n        cd /home/vagrant\n        tar -zxvf /tmp/vagrant-cache/gem.tar.gz\n      else\n        echo No new updates to /home/vagrant/.gem in cache...\n      fi\n    else\n      echo No /home/vagrant/.gem cache...\n    fi\n  SHELL\nend\n```\n\n##### 9.5.2.3.3. Automatically installing and removing the necessary Vagrant plugins\n\nVagrant can be extended by plugins and this class makes use of a number of them.  One of which, the `vagrant-vbguest` plugin is not installed if the Vagrant's base operating system is Alpine, but is installed otherwise.\n\n```ruby\nuninstall_plugins = %w( vagrant-cachier vagrant-alpine )\nrequired_plugins = %w( vagrant-timezone vagrant-proxyconf vagrant-certificates vagrant-disksize vagrant-reload ) # vagrant-disksize\n\nif (not os.downcase.include? 'alpine')\n  required_plugins = required_plugins \u003c\u003c \"vagrant-vbguest\"\nelse\n  # as alpine is currently not supported by vagrant-vbguest\n  uninstall_plugins = uninstall_plugins \u003c\u003c \"vagrant-vbguest\"\nend\n\n# Uninstall the following plugins\nplugin_uninstalled = false\nuninstall_plugins.each do |plugin|\n  if Vagrant.has_plugin?(plugin)\n    system \"vagrant plugin uninstall #{plugin}\"\n    plugin_uninstalled = true\n  end\nend\n\n# Require the following plugins\nplugin_installed = false\nrequired_plugins.each do |plugin|\n  unless Vagrant.has_plugin?(plugin)\n    system \"vagrant plugin install #{plugin}\"\n    plugin_installed = true\n  end\nend\n\n#system \"vagrant plugin update\"\n\n# if plugins were installed, restart\nif plugin_installed || plugin_uninstalled\n  puts \"restarting\"\n  exec \"vagrant #{ARGV.join' '}\"\nend\n```\n\n##### 9.5.2.3.4. Inserting Proxy setting via host environmental variables\n\nLater in the Vagrantfile, a bit of code makes use of the `vagrant-proxyconf` plugin to configure the HTTP proxy settings for the vagrants (i.e., the transient VMs).\n\n```ruby\n  # Set proxy settings for all vagrants\n  #\n  # Depends on install of vagrant-proxyconf plugin.\n  #\n  # To use:\n  #\n  # 1.  Install `vagrant plugin install vagrant-proxyconf`\n  # 2.  Set environmental variables for `http_proxy`, `https_proxy`, `ftp_proxy`, and `no_proxy`\n  #\n  #     For example:\n  #\n  #     ```\n  #     export http_proxy=\n  #     export https_proxy=\n  #     export ftp_proxy=\n  #     export no_proxy=\n  #     ```\n  if (ENV['http_proxy'] || ENV['https_proxy'])\n    config.proxy.http = ENV['http_proxy']\n    config.proxy.https = ENV['https_proxy']\n    config.proxy.ftp = ENV['ftp_proxy']\n    config.proxy.no_proxy = ENV['no_proxy']\n    config.proxy.enabled = { docker: false }\n\n    if ( ARGV.include? 'up' ) || ( ARGV.include? 'provision' )\n      puts \"INFO: HTTP Proxy variables set.\".green\n      puts \"INFO: http_proxy = #{ config.proxy.http }\".green\n      puts \"INFO: https_proxy = #{ config.proxy.https }\".green\n      puts \"INFO: ftp_proxy = #{ config.proxy.ftp }\".green\n      puts \"INFO: no_proxy = #{ config.proxy.no_proxy }\".green\n    end\n  else\n    if ( ARGV.include? 'up' ) || ( ARGV.include? 'provision' )\n      puts \"INFO: No http_proxy or https_proxy environment variables are set.\".green\n    end\n\n    config.proxy.http = nil\n    config.proxy.https = nil\n    config.proxy.ftp = nil\n    config.proxy.no_proxy = nil\n    config.proxy.enabled = false\n  end\n```\n\n##### 9.5.2.3.5. Inserting enterprise CA certificates\n\nLater still, a bit of code makes use of the `vagrant-certificates` plugin's to inject the specified certificates into the vagrants. This is useful, for example, if your enterprise network has a firewall (or appliance) which utilizes SSL interception. So, the existence of this plugin tell us more broadly others have to deal with the havoc SSL interception brings to development.\n\n```ruby\n  # To add Enterprise CA Certificates to all vagrants\n  #\n  # Depends on the install of the vagrant-certificates plugin\n  #\n  # To use:\n  #\n  # 1.  Install `vagrant plugin install vagrant-certificates`.\n  # 2.  Set environement variable for `CA_CERTIFICATES` containing a comma separated list of certificate URLs.\n  #\n  #     For example:\n  #\n  #     ```\n  #     export CA_CERTIFICATES=http://employeeshare.mitre.org/m/mjwalsh/transfer/MITRE%20BA%20ROOT.crt,http://employeeshare.mitre.org/m/mjwalsh/transfer/MITRE%20BA%20NPE%20CA-3%281%29.crt\n  #     ```\n  #\n  #     The Root certificate *must* be denotes as the root certificat like so:\n  #\n  #     http://employeeshare.mitre.org/m/mjwalsh/transfer/MITRE%20BA%20ROOT.crt\n  #\n\n  if ENV['CA_CERTIFICATES']\n    # Because @williambailey's vagrant-ca-certificates has an issue  https://github.com/williambailey/vagrant-ca-certificates/issues/34 I am using @Toilal fork, vagrant-certificates\n    if ( ARGV.include? 'up' ) || ( ARGV.include? 'provision' )\n      puts \"INFO: CA Certificates set to #{ ENV['CA_CERTIFICATES'] }\".green\n    end\n\n    config.certificates.enabled = true\n    config.certificates.certs = ENV['CA_CERTIFICATES'].split(',')\n  else\n    if ( ARGV.include? 'up' ) || ( ARGV.include? 'provision' )\n      puts \"INFO: No CA_CERTIFICATES environment variable set.\".green\n    end\n    config.certificates.certs = nil\n    config.certificates.enabled = false\n  end\n```\n\n##### 9.5.2.3.6. Auto-generate the Ansible inventory file\n\nThe Vagrantfile doesn't make use of the Vagrant's own [Ansible Local provisioner](https://www.vagrantup.com/docs/provisioning/ansible_local.html), but instead makes use of the `Shell` provisioner to install and use Ansible to configure the Vagrants.\n\nTo do this Ansible needs an inventory file and so the Vagrantfile dynamically creates the inventory file via the following code:\n\n```ruby\n# Write hosts file for Ansible\n\nrequire 'erb'\n\n@worker_nodes = [*1..ConfigurationVars::VARS[:nodes]-1]\n\ntemplate = \u003c\u003c-TEXT\n#\n# Do not change this file by hand as it is dynamically generated via the Vagrantfile.\n#\ndevelopment ansible_connection=local\nmaster ansible_connection=local\n\u003c% for @node in @worker_nodes %\u003enode\u003c%= @node %\u003e ansible_connection=local\n\u003c% end %\u003e\n[boxes]\nbox\n\n[masters]\nmaster\n\n[workers]\n\u003c% for @node in @worker_nodes %\u003enode\u003c%= @node %\u003e\n\u003c% end %\u003e\n[developments]\ndevelopment\nTEXT\n\nopen(File.join(vagrantfilePath, 'hosts'), 'w') do |f|\n f.puts ERB.new(template).result\nend\n```\n\nresulting in a `hosts` file being created in the root of the project upon `vagrant up` being called on the command-line.\n\nThe contents of the `hosts` file will resemeble:\n\n```\n#\n# Do not change this file by hand as it is dynamically generated via the Vagrantfile.\n#\ndevelopment ansible_connection=local\nmaster ansible_connection=local\n\n[boxes]\nbox\n\n[masters]\nmaster\n\n[workers]\n\n[developments]\ndevelopment\n```\n\n##### 9.5.2.3.7. Mounting the project folder into each vagrant\n\nEach vagrant (e.g., master, worker nodes if provisioned, development) will have the class project fodler mounted at `vagrant`.  If you or on OS X or Linux this will be done via the Network File Service requiring you to enter your password everytime prompted.  If you are on Windows the the mounting will be handled by VirtualBox.\n\nThe code in the Vagrantfile to accomplish this is the following lines:\n\n```ruby\n  # nfs does not appear to work reliably on OS X Catalina (See: https://github.com/hashicorp/vagrant/issues/11234)\n  if Vagrant::Util::Platform.windows?\n    config.vm.synced_folder '.', '/vagrant', owner: 'vagrant', group: 'vagrant', mount_options: ['dmode=775,fmode=664']\n  elsif Vagrant::Util::Platform.platform.include? 'darwin'\n    projectPath=File.join('/System/Volumes/Data/Users/', ENV['USER'], vagrantfilePath.split(ENV['USER']).last)\n\n    config.vm.synced_folder projectPath, '/vagrant', owner: 'vagrant', group: 'vagrant', mount_options: ['dmode=775,fmode=664']\n  else\n    config.vm.synced_folder '.',  '/vagrant', type: 'nfs'\n  end\n```\n\n**NOTE**\n\n- If you're host laptop or PC is using a version of Linux that Vagrant is unable configure `nfs` on, then you may have to comment out portions of this section in both `Vagrantfile` and `box/Vagrantfile`, so that the default `type` will be used like so\n\n  ```ruby\n  # nfs does not appear to work reliably on OS X Catalina (See: https://github.com/hashicorp/vagrant/issues/11234)\n  #  if Vagrant::Util::Platform.windows?\n  config.vm.synced_folder '.', '/vagrant', owner: 'vagrant', group: 'vagrant', mount_options: ['dmode=775,fmode=664']\n  #   elsif Vagrant::Util::Platform.platform.include? 'darwin'\n  #     projectPath=File.join('/System/Volumes/Data/Users/', ENV['USER'], vagrantfilePath.split(ENV['USER']).last)\n  #\n  #     config.vm.synced_folder projectPath, '/vagrant', owner: 'vagrant', group: 'vagrant', mount_options: ['dmode=775,fmode=664']\n  #   else\n  #     config.vm.synced_folder '.',  '/vagrant', type: 'nfs'\n  #   end\n  ```\n\n##### 9.5.2.3.8. Build a Vagrant Box\n\nThe master, worker nodes (if created) and development vagrant will need a base box.\n\nThe following section of code will call the `build_box.sh` shell script found in the `box` directory to create this box.\n\n```ruby\n  if ( ARGV.include? 'up' )\n    if (`vagrant box list | grep #{box}`.empty? )\n      puts \"INFO: Creating #{box} box...\".green\n      require 'open3'\n      Open3.popen2e('bash', '-c', 'cd box \u0026\u0026 ./build_box.sh') do |stdin, stdout, stderr|\n        puts stdout.each { |line| puts line }\n      end\n    else\n      puts \"INFO: Using existing #{box} box...\".green\n    end\n  end\n```\n\nVagrant needs a image to start from in much the same reason one builds an Amazon EC2 off an Amazon Machine Image (AMI) or VMWare Image.  Vagrant calls its images _Vagrant boxes_. One can create a Vagrant box via the vagrant command line interface (CLI) or alternatively one can use Hashicorp's Packer cli. The class use existing `centos/7` and `ubuntu/bionic` Vagrant boxes to create a box ontop of which the class vagrants (VMs) are built, but if you selected `nemonik/alpine310` as your `:base_box` in the `ConfigurationVars` and have istalled the `packer` cli packer will be used to create `nemonik/alpine310` base box and then take this box and use it to create a box via Vagrant ontop of which the vagrants will be created.\n\n##### 9.5.2.3.9. Configuring the Kubernetes cluster vagrant(s)\n\nBy default the class is configured as a two node cluster, a single master node and one worker node by having `:nodes` set to `2` in the `ConfigurationVars` module.\n\nThe Vagrant file uses the value of `:nodes` and the shell scripting templates defined in the `ConfigurationVars` module to create the cluster starting with a master node and each subsequent node being created as a worker node.\n\n```ruby\n  # shell scripting to install root user cached content\n  root_cached_template = ConfigurationVars::DETERMINE_OS_TEMPLATE + ConfigurationVars::OS_PACKAGES_FROM_CACHE_TEMPLATE + ConfigurationVars::SITE_PACKAGES_FROM_CACHE_TEMPLATE\n\n  # shell scripting to install user cached content\n  user_cached_template = ConfigurationVars::USER_CACHED_CONTENT_TEMPLATE\n\n  (0..ConfigurationVars::VARS[:nodes]-1).each do |node|\n\n    if (node == 0) then\n      hostname = 'master'\n      masters_root_cached = root_cached_template.gsub! /TYPE/, 'masters'\n      masters_user_cached = user_cached_template.gsub! /TYPE/, 'masters'\n    else\n      hostname = \"node#{node}\"\n      workers_root_cached = root_cached_template.gsub! /TYPE/, 'workers'\n      workers_user_cached = user_cached_template.gsub! /TYPE/, 'workers'\n    end\n\n    config.vm.define hostname do |vagrant|\n      vagrant.vm.network 'private_network', ip: \"#{ ConfigurationVars::VARS[:network_prefix] }.#{10 + node}\"\n      vagrant.vm.hostname = hostname\n\n      vagrant.vm.provider :virtualbox do |virtualbox|\n        virtualbox.name = \"Hands-on DevOps class - #{os} - #{hostname}\"\n        virtualbox.gui = false\n\n        # disable audio\n        virtualbox.customize ['modifyvm', :id, '--audio', 'none']\n\n        virtualbox.customize ['modifyvm', :id, '--nic1', 'nat']\n        virtualbox.customize ['modifyvm', :id, '--cableconnected1', 'on']\n        virtualbox.customize [\"modifyvm\", :id, \"--natdnshostresolver1\", \"on\"]\n        virtualbox.customize [\"modifyvm\", :id, \"--natdnsproxy1\", \"on\"]\n\n        virtualbox.customize [ \"guestproperty\", \"set\", :id, \"/VirtualBox/GuestAdd/VBoxService/--timesync-interval\", 10000 ]\n        virtualbox.customize [ \"guestproperty\", \"set\", :id, \"/VirtualBox/GuestAdd/VBoxService/--timesync-min-adjust\", 100 ]\n        virtualbox.customize [ \"guestproperty\", \"set\", :id, \"/VirtualBox/GuestAdd/VBoxService/--timesync-set-on-restore\", 1 ]\n        virtualbox.customize [ \"guestproperty\", \"set\", :id, \"/VirtualBox/GuestAdd/VBoxService/--timesync-set-start\", 1 ]\n        virtualbox.customize [ \"guestproperty\", \"set\", :id, \"/VirtualBox/GuestAdd/VBoxService/--timesync-set-threshold\", 1000 ]\n\n        if (node == 0) then\n          virtualbox.memory = 8192 #8192 #6144 #4096\n          virtualbox.cpus = 8 #8 #4\n        else\n          virtualbox.memory = 2048\n          virtualbox.cpus = 2\n        end\n\n        if (ConfigurationVars::VARS[:openebs_drives].downcase == 'yes') then # create OpenEBS drives on each node\n\n          openebs_disk = \"./#{hostname}_openebs_disk.vdi\"\n\n          # Add a second drive for OpenEBS\n          unless File.exist?(openebs_disk)\n            virtualbox.customize ['createmedium', '--filename', openebs_disk, '--size', ConfigurationVars::VARS[:openebs_drive_size_in_gb] * 1024]\n          end\n\n          # the value of storage_system_bus depends on your platform\n          storage_system_bus = \"IDE\"\n\n          # provisions the drive\n          virtualbox.customize ['storageattach', :id, '--storagectl', storage_system_bus, '--port', 1, '--device', 0, '--type', 'hdd', '--medium', openebs_disk]\n        end\n      end\n\n      # Configure via shell and Ansible\n\n      if (node == 0) then # the master node\n\n        # install root user cached content\n        vagrant.vm.provision 'root_cached_content', type: :shell, privileged: true, inline: \"#{masters_root_cached}\"\n\n        # install user cached content\n        vagrant.vm.provision 'user_cached_content', type: :shell, privileged: false, inline: \"#{masters_user_cached}\"\n\n        vagrant.vm.provision 'ansible', type: :shell, privileged: false, reset: true, inline: \u003c\u003c-SHELL\n          echo Configuring #{hostname} via Ansible...\n          cd /vagrant\n\n          n=0\n          until [ \"$n\" -ge #{ConfigurationVars::VARS[:default_retries]} ]; do\n            /home/vagrant/.local/bin/ansible-galaxy install --force --roles-path ansible/roles --role-file requirements.yml \u0026\u0026 break\n            n=$((n+1))\n            sleep #{ConfigurationVars::VARS[:default_delay]}\n          done\n\n          PYTHONUNBUFFERED=1 ANSIBLE_FORCE_COLOR=true /home/vagrant/.local/bin/ansible-playbook -vvvv --extra-vars=#{vars_string} --extra-vars='ansible_python_interpreter=\"/usr/bin/env #{ConfigurationVars::VARS[:ansible_python_version]}\"' --vault-password-file=vau","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnemonik%2Fhands-on-DevOps","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnemonik%2Fhands-on-DevOps","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnemonik%2Fhands-on-DevOps/lists"}