Full Code of Azure/Moodle for AI

master 2afa40361348 cached
92 files
1.1 MB
258.8k tokens
23 symbols
1 requests
Download .txt
Showing preview only (1,203K chars total). Download the full file or copy to clipboard to get everything.
Repository: Azure/Moodle
Branch: master
Commit: 2afa40361348
Files: 92
Total size: 1.1 MB

Directory structure:
gitextract_zdgud6lr/

├── .gitignore
├── .jshintrc
├── .travis.yml
├── CONTRIBUTE.md
├── Gruntfile.js
├── LICENSE
├── LICENSE-DOCS
├── README.md
├── SECURITY.md
├── azuredeploy-large-ha.json
├── azuredeploy-maximal.json
├── azuredeploy-minimal.json
├── azuredeploy-small2mid-noha.json
├── azuredeploy.json
├── azuredeploy.parameters.json
├── docs/
│   ├── Cleanup.md
│   ├── Deploy.md
│   ├── Environment-Variables.md
│   ├── Get-Install-Data.md
│   ├── Manage.md
│   ├── Parameters.md
│   ├── Preparation.md
│   ├── SslCert.md
│   ├── Test.md
│   └── env.json
├── env.json
├── etc/
│   ├── changeBranchInURL.sh
│   ├── checkBaseUrls.sh
│   ├── keyvault.sh
│   ├── travis/
│   │   ├── Configuration.py
│   │   ├── DeploymentTester.py
│   │   └── __init__.py
│   ├── travis.py
│   └── updateDocsParametersMd.sh
├── images/
│   └── Moodle-Architecture-PremiumFiles.vsdx
├── loadtest/
│   ├── Azure_Login.md
│   ├── Deploy_Load_Test_VM.md
│   ├── README.md
│   ├── azuredeploy.parameters.loadtest.defaults.json
│   ├── loadtest.sh
│   ├── moodle-on-azure-test-course-1.mbz
│   ├── simple-test-1.jmx
│   ├── simple-test-2.jmx
│   ├── time-gated-exam-test-dist-slaves.jmx
│   └── time-gated-exam-test.jmx
├── managedApplication/
│   ├── Cleanup.md
│   ├── DeployMoodleManagedApp.md
│   ├── Environment.md
│   ├── PublishMoodleManagedApplication.md
│   ├── README.md
│   ├── createServiceCatlogUpdate.sh
│   ├── createUIDefinition.json
│   └── parameters-template.json
├── metadata.json
├── migration/
│   ├── azure-fileshare-sa-deploy.json
│   └── azuredeploy-migration.json
├── nested/
│   ├── appgw.json
│   ├── controller.json
│   ├── controllersetup.json
│   ├── db-mssql.json
│   ├── db-mysql.json
│   ├── db-mysqlflex.json
│   ├── db-postgres.json
│   ├── gluster.json
│   ├── glustervm.json
│   ├── glustervmsetup.json
│   ├── network-subnets.json
│   ├── network-vnet-ddos.json
│   ├── network-vnet-privateDnsZone.json
│   ├── network-vnet.json
│   ├── network.json
│   ├── nfs-ha-vm.json
│   ├── nfs-ha.json
│   ├── recoveryservices.json
│   ├── recoveryservicesEnlist.json
│   ├── redis.json
│   ├── search-azure.json
│   ├── search-elastic-config.json
│   ├── search-elastic.json
│   ├── storageAccount.json
│   ├── tika.json
│   ├── tikaconfig.json
│   ├── vmsetupparams.json
│   └── webvmss.json
├── package.json
└── scripts/
    ├── helper_functions.sh
    ├── install_elastic.sh
    ├── install_gluster.sh
    ├── install_moodle.sh
    ├── install_tika.sh
    ├── setup_nfs_ha.sh
    └── setup_webserver.sh

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class

# C extensions
*.so

# Distribution / packaging
.Python
env/
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg

# PyInstaller
#  Usually these files are written by a python script from a template
#  before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
.hypothesis/

# Translations
*.mo
*.pot

# Django stuff:
*.log
local_settings.py

# Flask stuff:
instance/
.webassets-cache

# Scrapy stuff:
.scrapy

# Sphinx documentation
docs/_build/

# PyBuilder
target/

# Jupyter Notebook
.ipynb_checkpoints

# pyenv
.python-version

# celery beat schedule file
celerybeat-schedule

# SageMath parsed files
*.sage.py

# dotenv
.env

# virtualenv
.venv
venv/
ENV/

# Spyder project settings
.spyderproject
.spyproject

# Rope project settings
.ropeproject

# mkdocs documentation
/site

# mypy
.mypy_cache/

# test outputs
loadtest/test_outputs/

# Emacs
*#*#

# Node Modules
node_modules

# Eclipse
.project

# Specific azuredeploy.parameters.json files for dev testing (ignored not to expose ssh pub keys)
.params/
.vs/


================================================
FILE: .jshintrc
================================================
{
    "esnext": true,
    "node": true,
    "browser": true,
    "nomen": false,
    "bitwise": true,
    "eqeqeq": true,
    "forin": true,
    "immed": true,
    "latedef": true,
    "newcap": true,
    "noarg": true,
    "noempty": true,
    "nonew": true,
    "plusplus": true,
    "regexp": true,
    "undef": true,
    "unused": true,
    "trailing": true,
    "indent": 4,
    "esnext": true,
    "onevar": true,
    "white": true,
    "quotmark": "double",
    "predef": {
    }
}



================================================
FILE: .travis.yml
================================================
dist: trusty

language: python

node_js: "0.12"

python: "3.5"

cache:
  - directories: node_modules
  - pip

env:
  - PYTHONUNBUFFERED=TRUE

install:
  - npm install # Install task runners for lint checking.
  - pip install azure-mgmt-subscription azure-mgmt-resource keyring pycurl # Install Azure Python SDK (we only need the sub & the resource manager packages)

before_script:
  - ssh-keygen -q -f azure_moodle_id_rsa -N "" # Generate SSH keys to send to deployment

script:
  - npm test
  - ./etc/travis.py



================================================
FILE: CONTRIBUTE.md
================================================
# Contributing to Moodle on Azure 

The TL;DR version is:

  * We are a community project
  * We seek to make decisions through community consensus
  * We prefer debate through gradual improvement through pull requests to endless discussion about the "perfect" solution
  * We are a meritocracy, not a democracy
  * We welcome all your contributions including but not limited to feature requests, bug-reports, documentation and code
  
## How the project is managed

This project welcomes contributions and suggestions. Our goal is to
work on Azure specific tooling for deploying and managing the open
source [Moodle](http://moodle.org) learning management system on
Azure. We do not work on Moodle itself here, instead we work upstream
as appropriate.

The short version of how to contribute to this project is "just do
it". Where "it" can be defined as any valuable contribution (and to be
clear, asking questions is a valuable contribution):

  * ask questions
  * provide feedback
  * write or update documentation
  * help new users
  * recommend the project to others
  * test the code and report bugs
  * fix bugs and issue pull requests
  * give us feedback on required features
  * write and update the software
  * create artwork
  * translate to different languages
  * anything you can see that needs doing

Most contributions require you to agree to a Contributor License
Agreement (CLA) declaring that you have the right to, and actually do,
grant us the rights to use your contribution. For details, visit
https://cla.microsoft.com.

When you submit a pull request, a CLA-bot will automatically determine
whether you need to provide a CLA and decorate the PR appropriately
(e.g., label, comment). Simply follow the instructions provided by the
bot. You will only need to do this once across all repos using our
CLA.

## Decision Making

This is a community project. Decisions are made through consensus
building. All voices are equal and we welcome input from everyone.

That said, this is not a democracy. Consensus does not mean everyone
has to agree. It merely means that nobody is objecting *and* offering
an alternative.

What this means, in practive, is that she who does the work makes the
decisions. We'd rather discuss how to improve imperfect code than
argue over what would make perfect code. So if you have an objection
to the way we are doing things issue a pull request.

In the unlikely event that we cannot reach consensus through consensus
then the project maintainers (as identified by their having the admin
bit on GitHub) will make a judgetment call. But normally their
role is to guide the community to consensus action, not to make
decisions on bhalf of the community.

## Minimum Bar for Contributions

As the project matures we will add more thorough testing. It is expected
that all contributions pass the currently available suite of tests. If
they do not then they will be rejected.

It is also required that contributions which add features also bring
at least basic testing of that feature.

## Planning

This is an open source project. We have a few mantras to ensure
efficient collaboration, these mostly boil down to ensuring good
visibility into the communities goals. These include:

  * If it didn't happen in public, it didn't happen
  * Scratch your own itch
  
### If it didn't happen in public, it didn't happen (aka full transparency)

The goal of this mantra is to ensure maximum visibility into our
communities work in order to:

  1. Provide an opportunity for community feedback in order to ensure
     our plans are good
  2. Provide a clear indication of what will be done, what may be done
     and what won't be done
  
Both of these goals lead to the second mantra "Scratch your own itch".

### Scratch your own itch (aka getting what you want)

This is an open source project. We welcome feature requests and, as a
community, we will provide feedback on whether we intend to work on it
or not. To this end we categories feature requests in one of 4 ways:

  * Priority 0 (will address)
  * Priority 1 (may address)
  * Priority 2 (maybe one day)
  * wontfix (out of scope)

Using these priorities it is easy for community members to decide
where to spend their time. For example:

  * Priority 0 items are actively being worked on by at least one
    community member. Others are welcome to contribute as appropriate
    (reviews are particularly important)
  * Priority 1 items are seen as important and are likely to be worked
    on in the short to medium term, but there is no community member
    active on the project at this time. Community members are welcome
    to take ownership of these issues and propose a solution that they
    intend to implement. If the community accepts the proposal then it
    will become a Priority 0 issue.
  * Priority 2 items are seen as interesting proposals that are not in
    conflict with the projects goals but are unlikely to be worked on
    by any existing communty members. Community members who have a
    need for these items are strongly encouraged to identify
    themselves and offer a proposal for a solution. If there is enough
    support within the existing community this item can become a
    Priority 0 under your leadership.
  * Wontfix items are considered out of scope for this project.
    Community members should seek to solve the problem in different
    ways. Often this will mean contribution to Moodle itself or a
    plugin that is external to this community.

## Community roles

This section outlines roles and responsibilities within the community.

### Users

Users self-identify by using our software and documentation. Their
responsibilities are to benefit from our work, but we welcome
contributions from users, such as:

  * Ask questions
  * Answer questions
  * Feature requests
  * Bug reports
  * Design reviews
  * Planning reviews
  * Evangelize the project
  * and more...
  
Some users will become more involved with the project, those users
become Contributors.

### Contributors

Contributes self-identify by making longer term commitments to our
project. Their responsibilities are to help the project be succesful
by ensuring that our work matches the needs of our users.
Possible contributions can include:

  * Everything a User might contribute
  * Remove blocks for users
  * Provide design input
  * Review pull requests
  * Implement features
  * Triage questions, feature requests and bug reports
  * and more...
  
Some contributors will become very engaged and therefore become an
essential part of the community, these contributors will become
Maintainers.

### Maintainers

We are fans of efficient processes. Maintainers are people who insert
themselves into our process to ensure they run well. The goal is to
empower our contributors who in turn focus on delighting our users.
Maintainers contributions may include:

  * Everyting Users and Contributors do
  * Merge pull requests where appropriate
  * Seek community consensus where conflict occurs
  * Remove blocks for contributors
  * and more...

## Pull requests, Review and Merges

We like efficient processes. Anyone is welcome to issue pull requests.
Everyone is encouraged to review pull requests. Maintainers are
responsible for merging pull requests but they are not responsible for
reviews, that is a community wide responsibility.

We operate under two models of review process as appropriate to each
circumstance:

  * Merge then Review (our preferred model)
  * Review then Merge
  
### Merge Then Review

In the "merge then review" model a maintainer will merge the pull
request into with minimal review. Community members are still expected
to review the code, but it is done after the fact.

The goal is to get the code into a shared repository as early as
possible. This allows people, including advanced users, to start
testing it. This ensures we have the maximum possible exposure to
testing in real scenarios early in the process. Encouragin bug reports
from the whole community ensures we have visibility into breaks as
early as possible.

This model has its risks, however. If a PR is on the critical path or
it is controversial in some way it is expected that maintainers will
ensure it recieves a thorough review before merging (see next section
on "Review then Merge". This decision is at the discretion of the
maintainer who first triages the pull request.

Should a mistake be made and a bad merge be performed then it can
often be easier and faster to fix it under the "Merge then Review"
model than it is to provide feedback to the original author and await
a fix from them. Should the mistake have a high impact and/or no easy
fix is available we simply roll back the merge and provide feedback
via the review process.

It should be noted that this model means that maintainers have the
right to simply merge their own code and expect others to review it
*after*. Maintainers are expected to use their best judgement when
excercising this priviledge.

### Review Then Merge

Where a change is on the critical path or it is potentiall
contriversial maintainers should request reviews using the GitHub
tooling. The last reviewer to sign-off on the pull request will merge
the pull request.



================================================
FILE: Gruntfile.js
================================================
var grunt = require('grunt');
require('load-grunt-tasks')(grunt);

var templates = ['nested/*.json', 'managedApplication/*.json', 'loadtest/*.json', '*.json'];

grunt.initConfig({
  jshint: {
      files: templates,
      options: {
          jshintrc: '.jshintrc'
      }
  }
});
grunt.registerTask('test', ['jshint']);


================================================
FILE: LICENSE
================================================
The MIT License (MIT)
Copyright (c) Microsoft Corporation

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and 
associated documentation files (the "Software"), to deal in the Software without restriction, 
including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 
and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 
subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial 
portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT 
NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

================================================
FILE: LICENSE-DOCS
================================================
Attribution 4.0 International

=======================================================================

Creative Commons Corporation ("Creative Commons") is not a law firm and
does not provide legal services or legal advice. Distribution of
Creative Commons public licenses does not create a lawyer-client or
other relationship. Creative Commons makes its licenses and related
information available on an "as-is" basis. Creative Commons gives no
warranties regarding its licenses, any material licensed under their
terms and conditions, or any related information. Creative Commons
disclaims all liability for damages resulting from their use to the
fullest extent possible.

Using Creative Commons Public Licenses

Creative Commons public licenses provide a standard set of terms and
conditions that creators and other rights holders may use to share
original works of authorship and other material subject to copyright
and certain other rights specified in the public license below. The
following considerations are for informational purposes only, are not
exhaustive, and do not form part of our licenses.

     Considerations for licensors: Our public licenses are
     intended for use by those authorized to give the public
     permission to use material in ways otherwise restricted by
     copyright and certain other rights. Our licenses are
     irrevocable. Licensors should read and understand the terms
     and conditions of the license they choose before applying it.
     Licensors should also secure all rights necessary before
     applying our licenses so that the public can reuse the
     material as expected. Licensors should clearly mark any
     material not subject to the license. This includes other CC-
     licensed material, or material used under an exception or
     limitation to copyright. More considerations for licensors:
	wiki.creativecommons.org/Considerations_for_licensors

     Considerations for the public: By using one of our public
     licenses, a licensor grants the public permission to use the
     licensed material under specified terms and conditions. If
     the licensor's permission is not necessary for any reason--for
     example, because of any applicable exception or limitation to
     copyright--then that use is not regulated by the license. Our
     licenses grant only permissions under copyright and certain
     other rights that a licensor has authority to grant. Use of
     the licensed material may still be restricted for other
     reasons, including because others have copyright or other
     rights in the material. A licensor may make special requests,
     such as asking that all changes be marked or described.
     Although not required by our licenses, you are encouraged to
     respect those requests where reasonable. More_considerations
     for the public: 
	wiki.creativecommons.org/Considerations_for_licensees

=======================================================================

Creative Commons Attribution 4.0 International Public License

By exercising the Licensed Rights (defined below), You accept and agree
to be bound by the terms and conditions of this Creative Commons
Attribution 4.0 International Public License ("Public License"). To the
extent this Public License may be interpreted as a contract, You are
granted the Licensed Rights in consideration of Your acceptance of
these terms and conditions, and the Licensor grants You such rights in
consideration of benefits the Licensor receives from making the
Licensed Material available under these terms and conditions.


Section 1 -- Definitions.

  a. Adapted Material means material subject to Copyright and Similar
     Rights that is derived from or based upon the Licensed Material
     and in which the Licensed Material is translated, altered,
     arranged, transformed, or otherwise modified in a manner requiring
     permission under the Copyright and Similar Rights held by the
     Licensor. For purposes of this Public License, where the Licensed
     Material is a musical work, performance, or sound recording,
     Adapted Material is always produced where the Licensed Material is
     synched in timed relation with a moving image.

  b. Adapter's License means the license You apply to Your Copyright
     and Similar Rights in Your contributions to Adapted Material in
     accordance with the terms and conditions of this Public License.

  c. Copyright and Similar Rights means copyright and/or similar rights
     closely related to copyright including, without limitation,
     performance, broadcast, sound recording, and Sui Generis Database
     Rights, without regard to how the rights are labeled or
     categorized. For purposes of this Public License, the rights
     specified in Section 2(b)(1)-(2) are not Copyright and Similar
     Rights.

  d. Effective Technological Measures means those measures that, in the
     absence of proper authority, may not be circumvented under laws
     fulfilling obligations under Article 11 of the WIPO Copyright
     Treaty adopted on December 20, 1996, and/or similar international
     agreements.

  e. Exceptions and Limitations means fair use, fair dealing, and/or
     any other exception or limitation to Copyright and Similar Rights
     that applies to Your use of the Licensed Material.

  f. Licensed Material means the artistic or literary work, database,
     or other material to which the Licensor applied this Public
     License.

  g. Licensed Rights means the rights granted to You subject to the
     terms and conditions of this Public License, which are limited to
     all Copyright and Similar Rights that apply to Your use of the
     Licensed Material and that the Licensor has authority to license.

  h. Licensor means the individual(s) or entity(ies) granting rights
     under this Public License.

  i. Share means to provide material to the public by any means or
     process that requires permission under the Licensed Rights, such
     as reproduction, public display, public performance, distribution,
     dissemination, communication, or importation, and to make material
     available to the public including in ways that members of the
     public may access the material from a place and at a time
     individually chosen by them.

  j. Sui Generis Database Rights means rights other than copyright
     resulting from Directive 96/9/EC of the European Parliament and of
     the Council of 11 March 1996 on the legal protection of databases,
     as amended and/or succeeded, as well as other essentially
     equivalent rights anywhere in the world.

  k. You means the individual or entity exercising the Licensed Rights
     under this Public License. Your has a corresponding meaning.


Section 2 -- Scope.

  a. License grant.

       1. Subject to the terms and conditions of this Public License,
          the Licensor hereby grants You a worldwide, royalty-free,
          non-sublicensable, non-exclusive, irrevocable license to
          exercise the Licensed Rights in the Licensed Material to:

            a. reproduce and Share the Licensed Material, in whole or
               in part; and

            b. produce, reproduce, and Share Adapted Material.

       2. Exceptions and Limitations. For the avoidance of doubt, where
          Exceptions and Limitations apply to Your use, this Public
          License does not apply, and You do not need to comply with
          its terms and conditions.

       3. Term. The term of this Public License is specified in Section
          6(a).

       4. Media and formats; technical modifications allowed. The
          Licensor authorizes You to exercise the Licensed Rights in
          all media and formats whether now known or hereafter created,
          and to make technical modifications necessary to do so. The
          Licensor waives and/or agrees not to assert any right or
          authority to forbid You from making technical modifications
          necessary to exercise the Licensed Rights, including
          technical modifications necessary to circumvent Effective
          Technological Measures. For purposes of this Public License,
          simply making modifications authorized by this Section 2(a)
          (4) never produces Adapted Material.

       5. Downstream recipients.

            a. Offer from the Licensor -- Licensed Material. Every
               recipient of the Licensed Material automatically
               receives an offer from the Licensor to exercise the
               Licensed Rights under the terms and conditions of this
               Public License.

            b. No downstream restrictions. You may not offer or impose
               any additional or different terms or conditions on, or
               apply any Effective Technological Measures to, the
               Licensed Material if doing so restricts exercise of the
               Licensed Rights by any recipient of the Licensed
               Material.

       6. No endorsement. Nothing in this Public License constitutes or
          may be construed as permission to assert or imply that You
          are, or that Your use of the Licensed Material is, connected
          with, or sponsored, endorsed, or granted official status by,
          the Licensor or others designated to receive attribution as
          provided in Section 3(a)(1)(A)(i).

  b. Other rights.

       1. Moral rights, such as the right of integrity, are not
          licensed under this Public License, nor are publicity,
          privacy, and/or other similar personality rights; however, to
          the extent possible, the Licensor waives and/or agrees not to
          assert any such rights held by the Licensor to the limited
          extent necessary to allow You to exercise the Licensed
          Rights, but not otherwise.

       2. Patent and trademark rights are not licensed under this
          Public License.

       3. To the extent possible, the Licensor waives any right to
          collect royalties from You for the exercise of the Licensed
          Rights, whether directly or through a collecting society
          under any voluntary or waivable statutory or compulsory
          licensing scheme. In all other cases the Licensor expressly
          reserves any right to collect such royalties.


Section 3 -- License Conditions.

Your exercise of the Licensed Rights is expressly made subject to the
following conditions.

  a. Attribution.

       1. If You Share the Licensed Material (including in modified
          form), You must:

            a. retain the following if it is supplied by the Licensor
               with the Licensed Material:

                 i. identification of the creator(s) of the Licensed
                    Material and any others designated to receive
                    attribution, in any reasonable manner requested by
                    the Licensor (including by pseudonym if
                    designated);

                ii. a copyright notice;

               iii. a notice that refers to this Public License;

                iv. a notice that refers to the disclaimer of
                    warranties;

                 v. a URI or hyperlink to the Licensed Material to the
                    extent reasonably practicable;

            b. indicate if You modified the Licensed Material and
               retain an indication of any previous modifications; and

            c. indicate the Licensed Material is licensed under this
               Public License, and include the text of, or the URI or
               hyperlink to, this Public License.

       2. You may satisfy the conditions in Section 3(a)(1) in any
          reasonable manner based on the medium, means, and context in
          which You Share the Licensed Material. For example, it may be
          reasonable to satisfy the conditions by providing a URI or
          hyperlink to a resource that includes the required
          information.

       3. If requested by the Licensor, You must remove any of the
          information required by Section 3(a)(1)(A) to the extent
          reasonably practicable.

       4. If You Share Adapted Material You produce, the Adapter's
          License You apply must not prevent recipients of the Adapted
          Material from complying with this Public License.


Section 4 -- Sui Generis Database Rights.

Where the Licensed Rights include Sui Generis Database Rights that
apply to Your use of the Licensed Material:

  a. for the avoidance of doubt, Section 2(a)(1) grants You the right
     to extract, reuse, reproduce, and Share all or a substantial
     portion of the contents of the database;

  b. if You include all or a substantial portion of the database
     contents in a database in which You have Sui Generis Database
     Rights, then the database in which You have Sui Generis Database
     Rights (but not its individual contents) is Adapted Material; and

  c. You must comply with the conditions in Section 3(a) if You Share
     all or a substantial portion of the contents of the database.

For the avoidance of doubt, this Section 4 supplements and does not
replace Your obligations under this Public License where the Licensed
Rights include other Copyright and Similar Rights.


Section 5 -- Disclaimer of Warranties and Limitation of Liability.

  a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE
     EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS
     AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF
     ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS,
     IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION,
     WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
     PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS,
     ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT
     KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT
     ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU.

  b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE
     TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,
     NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT,
     INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES,
     COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR
     USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN
     ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR
     DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR
     IN PART, THIS LIMITATION MAY NOT APPLY TO YOU.

  c. The disclaimer of warranties and limitation of liability provided
     above shall be interpreted in a manner that, to the extent
     possible, most closely approximates an absolute disclaimer and
     waiver of all liability.


Section 6 -- Term and Termination.

  a. This Public License applies for the term of the Copyright and
     Similar Rights licensed here. However, if You fail to comply with
     this Public License, then Your rights under this Public License
     terminate automatically.

  b. Where Your right to use the Licensed Material has terminated under
     Section 6(a), it reinstates:

       1. automatically as of the date the violation is cured, provided
          it is cured within 30 days of Your discovery of the
          violation; or

       2. upon express reinstatement by the Licensor.

     For the avoidance of doubt, this Section 6(b) does not affect any
     right the Licensor may have to seek remedies for Your violations
     of this Public License.

  c. For the avoidance of doubt, the Licensor may also offer the
     Licensed Material under separate terms or conditions or stop
     distributing the Licensed Material at any time; however, doing so
     will not terminate this Public License.

  d. Sections 1, 5, 6, 7, and 8 survive termination of this Public
     License.


Section 7 -- Other Terms and Conditions.

  a. The Licensor shall not be bound by any additional or different
     terms or conditions communicated by You unless expressly agreed.

  b. Any arrangements, understandings, or agreements regarding the
     Licensed Material not stated herein are separate from and
     independent of the terms and conditions of this Public License.


Section 8 -- Interpretation.

  a. For the avoidance of doubt, this Public License does not, and
     shall not be interpreted to, reduce, limit, restrict, or impose
     conditions on any use of the Licensed Material that could lawfully
     be made without permission under this Public License.

  b. To the extent possible, if any provision of this Public License is
     deemed unenforceable, it shall be automatically reformed to the
     minimum extent necessary to make it enforceable. If the provision
     cannot be reformed, it shall be severed from this Public License
     without affecting the enforceability of the remaining terms and
     conditions.

  c. No term or condition of this Public License will be waived and no
     failure to comply consented to unless expressly agreed to by the
     Licensor.

  d. Nothing in this Public License constitutes or may be interpreted
     as a limitation upon, or waiver of, any privileges and immunities
     that apply to the Licensor or You, including from the legal
     processes of any jurisdiction or authority.


=======================================================================

Creative Commons is not a party to its public
licenses. Notwithstanding, Creative Commons may elect to apply one of
its public licenses to material it publishes and in those instances
will be considered the “Licensor.” The text of the Creative Commons
public licenses is dedicated to the public domain under the CC0 Public
Domain Dedication. Except for the limited purpose of indicating that
material is shared under a Creative Commons public license or as
otherwise permitted by the Creative Commons policies published at
creativecommons.org/policies, Creative Commons does not authorize the
use of the trademark "Creative Commons" or any other trademark or logo
of Creative Commons without its prior written consent including,
without limitation, in connection with any unauthorized modifications
to any of its public licenses or any other arrangements,
understandings, or agreements concerning use of licensed material. For
the avoidance of doubt, this paragraph does not form part of the
public licenses.

Creative Commons may be contacted at creativecommons.org.

================================================
FILE: README.md
================================================

# Deploy and Manage a Scalable Moodle Cluster on Azure

This repository contains guides and [Azure Resource Manager](https://docs.microsoft.com/en-us/azure/azure-resource-manager/resource-group-overview) templates designed to help you deploy and manage a highly available and scalable
[Moodle](https://moodle.com) cluster on Azure. In addition, the repository contains other useful information relevant to running Moodle on Azure such as a listing of Azure-relevant Moodle plugins and information on how to offer Moodle as a Managed Application on the Azure Marketplace or on an IT Service Catalog.

If you have an Azure account you can deploy Moodle via the [Azure portal](https://portal.azure.com) using the button below, or you can [deploy Moodle via the
CLI](docs/Deploy.md). Please note that while you can use an [Azure free account](https://azure.microsoft.com/en-us/free/) to get started depending on which template configuration you choose you will likely be required to upgrade to a paid account.

## Deployment Introduction

In the table below, we provide a number of default configurations at different scales of operation. These options minimize the configuration you would otherwise need to do manually; these options are essentially "good practice" recommendations. Once deployed, you will have full access to the Azure resources and can adjust the deployment to suit your needs. If you would prefer to have full control over all the configuration options at deployment, please refer to [the fully configurable section](#Fully Configurable) right after the Predefined deployment option section.

## SSH Key Requirement

All of the deployment options require you to provide a valid SSH protocol 2 (SSH-2) RSA public-private key pairs with a minimum length of 2048 bits. Other key formats such as ED25519 and ECDSA are not supported.

If you are unfamiliar with SSH and SSH keys, read this [article](https://docs.microsoft.com/en-us/azure/virtual-machines/linux/mac-create-ssh-keys) which will explain how to generate a key pair.  You will create a ssh key pair. The public key is copied to the instances via the template. The private key is your identity that you will use to connect to different parts of the service.

## Predefined deployment options

Below are a list of pre-defined/restricted deployment options based on typical deployment scenarios (i.e. dev/test, production etc.) All configurations are fixed and you just need to pass your ssh public key to the template so that you can log in to the deployed VMs.

| Deployment Type | Description | Launch |
| --- | --- | ---|
| Minimal  | This deployment will use NFS, Azure Database for MySQL Flexible Server (Burstable SKU 2 vCores), and smaller autoscale web frontend VM sku (1 core) that'll give faster deployment time (less than 30 minutes) and requires only 2 VM cores currently that'll fit even in a free trial Azure subscription.|[![Deploy to Azure Minimally](http://azuredeploy.net/deploybutton.png)](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2FAzure%2FMoodle%2Fmaster%2Fazuredeploy-minimal.json)|
| Small to Mid-Size | Supporting up to 1000 concurrent users.  This deployment will use NFS (no high availability) and Azure Database for MySQL Flexible Server(General Purpose SKU 8 vCores), without other options like elastic search or redis cache.|[![Deploy to Azure Minimally](http://azuredeploy.net/deploybutton.png)](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2FAzure%2FMoodle%2Fmaster%2Fazuredeploy-small2mid-noha.json)|
|Large size deployment (with high availability)| Supporting more than 2000 concurrent users. This deployment will use Azure Premium Files, Azure Database for MySQL Flexible Server (General Purpose SKU 16 vCores) and redis cache, without other options like elastic search. |[![Deploy to Azure Minimally](http://azuredeploy.net/deploybutton.png)](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2FAzure%2FMoodle%2Fmaster%2Fazuredeploy-large-ha.json)|
| Maximum |This maximal deployment will use Azure Premium Files, Azure Database for MySQL (Business Critical SKU 64 vCores), redis cache, elastic search (3 VMs), and pretty large storage sizes (both data disks and DB).|[![Deploy to Azure Maximally](http://azuredeploy.net/deploybutton.png)](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2FAzure%2FMoodle%2Fmaster%2Fazuredeploy-maximal.json)|

**NOTE**: The above deployment templates use hard coded Azure Database for MySQL Flexible Server SKUs for easier configuration and quicker deployment of Moodle workloads. If your deployment fails for any reason, please revert to the fully configurable template where possible and change the Azure Database for MySQL Flexible Server parameters accordingly.

## Fully Configurable

If you would prefer to configure the deployment right at the start of the process, you use the button below. Please note that this method opens up a large number of parameters to configure and users new to this deployment process may find it overwhelming. It is also very likely you may end up with a deployment configuration that is not optimal to your needs. This method is recommended for power users.

[![Deploy to Azure Fully Configurable](http://azuredeploy.net/deploybutton.png)](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2FAzure%2FMoodle%2Fmaster%2Fazuredeploy.json)  [![Visualize](https://raw.githubusercontent.com/Azure/azure-quickstart-templates/master/1-CONTRIBUTION-GUIDE/images/visualizebutton.png)](http://armviz.io/#/?load=https%3A%2F%2Fraw.githubusercontent.com%2FAzure%2FMoodle%2Fmaster%2Fazuredeploy.json)

**NOTE**: Before you deploy your Moodle workloads using a fully configurable template, we suggest reviewing [Azure Database for MySQL Flexible Server](https://learn.microsoft.com/en-us/azure/mysql/flexible-server/) documentation to fully understand the parameters and the options suggested in the parameters to pick the right values for your workload needs.

## Stack Architecture

This template set deploys the following infrastructure core to your Moodle instance:

- Autoscaling web frontend layer (Nginx for https termination, Varnish for caching, Apache/php or nginx/php-fpm)
- Private virtual network for frontend instances
- Controller instance running cron and handling syslog for the autoscaled site
- [Azure Load balancer](https://azure.microsoft.com/en-us/services/load-balancer/) to balance across the autoscaled instances
- [Azure Database for MySQL](https://azure.microsoft.com/en-us/services/mysql/) or [Azure Database for PostgreSQL](https://azure.microsoft.com/en-us/services/postgresql/) or [Azure SQL Database](https://azure.microsoft.com/en-us/services/sql-database/)
- For large production deployments Azure Premium Files is recommended for file storage as it provides higher performance and availability with the addition of metadata-caching [https://learn.microsoft.com/en-us/azure/storage/files/smb-performance#metadata-caching-for-premium-smb-file-shares](https://learn.microsoft.com/en-us/azure/storage/files/smb-performance#metadata-caching-for-premium-smb-file-shares)

This template set *optionally* configures the following additional infrastructure:

- [Azure Backup](https://azure.microsoft.com/en-us/services/backup/) for Moodle site backups
- [Azure Blob Storage](https://azure.microsoft.com/en-us/services/storage/blobs/) for ObjectFS (Moodle sitedata)
- [Azure Application Gateway](https://azure.microsoft.com/en-us/services/application-gateway/) for SSL offloading and WAF
- [Azure Redis Cache](https://azure.microsoft.com/en-us/services/cache/) instance for Moodle caching
- [Azure DDoS Protection](https://azure.microsoft.com/en-us/services/ddos-protection/) plan to secure your Moodle site from DDoS attacks
- [Azure Key Vault](https://azure.microsoft.com/en-us/services/key-vault/) for storing your CA Cert for your Moodle site
- [Azure Search](https://azure.microsoft.com/en-us/services/search/) instance or three Elasticsearch VMs for HA Global Search in Moodle
- [Apache Tika](http://tika.apache.org/) VMs for search indexing in Moodle

![network_diagram](images/Moodle-Architecture-PremiumFiles.png "Diagram of deployed stack")

The template also optionally installs plugins that allow Moodle to be integrated with select Azure services (see below for details).

## Useful Moodle plugins for integrating Moodle with Azure Services

There below is a listing of useful plugins allow Moodle to be integrated with select Azure services:

- [Object File System Plugin*](https://github.com/catalyst/moodle-tool_objectfs) for [Azure Blob Storage](https://azure.microsoft.com/en-us/services/storage/blobs/)
- [Azure Search Plugin*](https://github.com/catalyst/moodle-search_azure) for [Azure Search](https://azure.microsoft.com/en-us/services/logic-apps/)
- [Trigger Plugin](https://github.com/catalyst/moodle-tool_trigger) and [Restful Webservice Plugin](https://github.com/catalyst/moodle-webservice_restful) for [Azure Logic Apps](https://azure.microsoft.com/en-us/services/logic-apps/) (requires use of [Moodle Connector](https://github.com/catalyst/azure-connector_moodle) now in development)
- [Office 365 and Microsoft Entra ID (formerly Azure Active Directory) Plugins for Moodle*](https://github.com/Microsoft/o365-moodle) for [Microsoft Entra ID](https://www.microsoft.com/en-us/security/business/identity-access/microsoft-entra-id/)
- [Elasticsearch Plugin*](https://github.com/catalyst/moodle-search_elastic)

At the current time this template allows the optional installation of the plugins above with a * next to them. Please note these plugins can be installed at any time post deployment via Moodle's own [plugin directory](https://moodle.org/plugins/). You can find a list of all Azure relevant plugins in the Moodle plugin directory [here](https://moodle.org/plugins/browse.php?list=set&id=91). You might also choose to follow this list via RSS.

## Moodle as a Managed Application

You can learn more about how you can offer Moodle as a Managed Application on the Azure Marketplace or on an IT Service Catalog [here](https://github.com/Azure/Moodle/tree/master/managedApplication). This is a great read if you are offering Moodle hosting services today for your customers.

## Observations about the current template

The template is highly configurable. Full details of the configuration options can be found in our [documentation](https://github.com/Azure/Moodle/tree/master/docs) (more specifically in our [parameters documentation](https://github.com/Azure/Moodle/blob/master/docs/Parameters.md)). The following sections describe observations about the template that you will likely want to review before deploying:

**Scalability** Our system is designed to be highly scalable. To achieve this we provide a Virtual Machine Scaleset for the web tier. This is already configured to scale on high load. However, scaling the VMs is not instantaneous. If you know you will have a high-load situation(e.g. exam, you should manually scale the VMs prior to the event. This can be done through the Azure portal or the CLI. The database is less easily scaled at this point, but it is possible and documented in our [management documentation](https://github.com/Azure/Moodle/blob/master/docs/Manage.md#resizing-your-database).

**SSL** The template fully supports SSL but it is not possible for the template to manage this for you. More information in our [managing certs documentation](https://github.com/Azure/Moodle/blob/master/docs/SslCert.md).

**Moodle PHP Code** The Moodle PHP code is stored on the Controller VM and copied to each front end VM upon deployment and upon request (should you update the Moodle code with your own code). For more information see our [management documentation](https://github.com/Azure/Moodle/blob/master/docs/Manage.md#updating-moodle-codesettings).

**Database** Currently the best performance is achieved with [Azure Database for MySQL](https://azure.microsoft.com/en-us/services/mysql/) and [Azure SQL Database](https://azure.microsoft.com/en-us/services/sql-database/). With [Azure Database for PostgreSQL](https://azure.microsoft.com/en-us/services/postgresql/) we have hit database constraints which caused processes to load up on the frontends until they ran out of memory. It is possible some PostgreSQL tuning might help here. Above pre-configured deployment templates deploy Azure Database for MySQL Flexible Server in a VNet. For configuring Azure Database for MySQL Flexible Server outside a VNet to use firewall-based IP restriction, please use the fully configurable template.

**File Storage** There are two options for file storage (moodledata) - Azure Premium Files or NFS. Azure Premium Files is recommended for production deployments as it provides higher performance and availability with the addition of metadata-caching [https://learn.microsoft.com/en-us/azure/storage/files/smb-performance#metadata-caching-for-premium-smb-file-shares](https://learn.microsoft.com/en-us/azure/storage/files/smb-performance#metadata-caching-for-premium-smb-file-shares). Non-HA NFS is recommended for dev/test deployments.

**Search.** Azure supports running an Elasticsearch cluster, however it does not offer a fully-managed Elasticsearch service, so for those looking for a fully-managed Search service [Azure Search](https://azure.microsoft.com/en-us/services/logic-apps/) is recommended.

**Caching.** While enabling Redis cache can improve performance for a large Moodle site we have not seen it be very effective for small-to-medium size sites. We can likely improve upon this, patches welcome ;-)

**Regions.** Note that not all resources types (such as databases) may be available in your region. You should check the list of [Azure Products by Region](https://azure.microsoft.com/en-us/global-infrastructure/services/) to for local availability.

## Common questions about this Template

1. **Is this template Moodle as IaaS or PaaS?**  While the current template leverages PaaS services such as Redis, Azure Database for MySQL Flexible Server, Azure Database for Postgres, MS SQL etc. the current template offers Moodle as IaaS. Given limitations to Moodle our focus is IaaS for the time being however we would love to be informed of your experience running Moodle as PaaS on Azure (i.e. using [Azure Container Service](https://azure.microsoft.com/en-us/services/container-service/) or [Azure App Service](https://azure.microsoft.com/en-us/services/container-service/)).

2. **The current template uses Ubuntu. Will other Operating Systems such as CentOS or Windows Server be supported in the future?** Unfortunately we only have plans to support Ubuntu at this time. It is highly unlikely that this will change.

3. **What configuration do you recommend for my Moodle site?** The answer is it depends. At this stage we provide some rudimentary t-shirt sized deployment recommendations and we are still building out our load testing tools and methodologies to provide more granularity. With that being said this is an area we are investing heavily in this area and we would love your contributions (i.e. load testing scripts, tools, methodologies etc.).

  If you have an immediate need for guidance for a larger sized deployment, you might want to share some details around your deployment on our [issues page](https://github.com/Azure/Moodle/issues) and we will do our best to respond. Please share as much information about your deployment as possible such as:

    - average number of concurrent users your site will see
    - maximum level of concurrent/simultaneous users your site needs to support
    - whether or not HA is needed
    - any other attributes specific to your deployment (i.e. load balancing across regions etc.)

4. **Did Microsoft build this template alone or with the help of the Moodle community?** We did not build this template alone. We relied on the expertise and guidance of many capable Moodle partners around the world. The initial implementation of the template was done by [Catalyst IT](https://github.com/catalyst).

5. **How does this template relate to other Moodle offerings available on the Azure Marketplace?** It is generally not a good idea to run Moodle as a single VM in a production setting. This template is highly configurable and allows for high availability and redundancy.

6. **How does this template relate to this [Azure Quickstart Template for Moodle](https://github.com/Azure/azure-quickstart-templates/tree/master/application-workloads/moodle/moodle-scalable-cluster-ubuntu)?** This repo is the working repo for the quickstart template. We will be pushing changes from this template to the quickstart template on a regular cadence.

7. **I am already running Moodle on Azure. How does this work benefit me?** We are looking for painpoints from you and the broader Moodle on Azure community that we can help solve. We are also looking to understand where our implementation of Moodle on Azure outperforms or underperforms other implementations such as yours that are out in the wild. If you have observations, performance benchmarks or just general feedback about your experience running Moodle on Azure that you'd like to share we're extremely interested! Load testing is a very big area of focus, so if you have scripts you wouldn't mind contributing please let us know.

8. **Has anyone run this template sucessfully in production?** Yes they have. With that being said, we do not make any performance guarantees about this architecture.

9. **What type of improvements have you succeeded in making** Since we first began this effort we have managed to make great gains, achieving a >2x performance boost from our original configuration by making tweaks to things like where PHP files were stored. Our work is nowhere near over.  

10. **What other Azure services (i.e. [Azure CDN](https://azure.microsoft.com/en-us/services/cdn/), [Azure Media Services](https://azure.microsoft.com/en-us/services/media-services/), [Azure Bot Service](https://azure.microsoft.com/en-us/services/bot-service/) etc.) will you be integrating with when this effort is complete?** It's not clear yet. We'll need your [feedback](https://github.com/Azure/Moodle/issues) to decide.

11. **Why is the database on a public subnet?** At this stage only Azure Database for PostgreSQL do not support being moved to a vnet. As a workaround, we use a firewall-based IP restriction allow access only to the controller VM and VMSS load-balancer IPs.  

12. **Is Azure Database for MySQL Flexible Server deployed in a VNet?** When you leverage one of the pre-defined template options, we automatically deploy your Azure Database for MySQL Flexible Server in VNet for better isolation and greater security, optionally you can choose the fully configurable template to deploy Azure Database for MySQL Flexible Server outside VNet depending on your needs.

13. **How can I help with this effort?** Please see below.

## Automated Testing (Travis CI)

This repository uses [Travis CI](https://travis-ci.org/) to deliver automated testing.

The following tests are carried out for every Pull Request and will also run in a Travis CI enabled forked repository:

- **JSON Linting** - All JSON files are linted to ensure they do not contain any syntax errors.
- **JSON Code Style** - All JSON files are tested to ensure they comply with project code style rules.

The following tests are carried out as part of the Pull Request merging prior to a contribution being accepted into the release branch:

- **Template Validation** - The template is submitted to Azure to ensure it is correctly formatted and contains valid logic.
- **Template Build** - The template is submitted to Azure and the stack described in the template is built to ensure a stack is correctly deployed.

### Setting Up Travis CI for Template Build

The following describes the process required if you want to run the template validation and build steps using your own Travis and Azure accounts.

To set up the build process, you will need:

- An Azure account or active subscription
- A fork of this repository linked to Travis CI
- Access to an installed instance of the Azure CLI
- A SSH key-pair

The Travis CI process uses the *Azure CLI Service Principal* login method to authenticate against Azure. The documentation for logging in via a Service Principal can be found here: [https://docs.microsoft.com/en-us/cli/azure/authenticate-azure-cli?view=azure-cli-latest#logging-in-with-a-service-principal](https://docs.microsoft.com/en-us/cli/azure/authenticate-azure-cli?view=azure-cli-latest#logging-in-with-a-service-principal)

Before you can log in using the Service Principal process you need to create a *Service Principal*. The documentation to create a Service Principal login can be found here: [https://docs.microsoft.com/en-us/cli/azure/create-an-azure-service-principal-azure-cli](https://docs.microsoft.com/en-us/cli/azure/create-an-azure-service-principal-azure-cli)

When a Service Principal is created using the Azure CLI a JSON response is returned containing:

- **name** - This is the Service Principal username.
- **password** - This is the Service Principal password.
- **tenantId** - This is the Service Principal tenant unique ID.

You will need these three above values to have Travis and Azure deploy and test your template.

The next step is to take the above values returned by the Service Principal creation and use them to define *environment variables* in Travis CI.

The following link shows how to set up per repository environment variables in Travis CI: [https://docs.travis-ci.com/user/environment-variables/#Defining-Variables-in-Repository-Settings](https://docs.travis-ci.com/user/environment-variables/#Defining-Variables-in-Repository-Settings) Using this documentation set up the following three *hidden* environment variables in Travis CI for your fork of this repository.

- **SPNAME** - The value of the *name* parameter returned by the Service Principal create process.
- **SPPASSWORD** - The value of the *password* parameter returned by the Service Principal create process.
- **SPTENANT** - The value of the *tenant* parameter returned by the Service Principal create process.
- **SPSSHKEY** *(default: generate new)*- A public SSH key that you have the corresponding private key for. This is currently not used but is required for the build to be successful.
- **LOCATION** *(default: southcentralus)*- Location for the test resource group.
- **RESOURCEGROUP** *(default: azmdl-travis-XXX)*- Name to use for the resource group.
- **FULLCI_BRANCHES** *(default: master)*- Name of branches (separated by ':') to always run FULL CI (if credentials are provided). Full CI will run a deployment test which will create and use resources from your Azure account.

**NOTE:** You can trigger a full CI test by adding *[full ci]* or *[fullci]* anywhere in the commit message.  

**NOTE:** Make sure you set the environment variables to hidden otherwise they will be exposed publicly at run time.

**NOTE:** As per the Travis CI documentation make sure you have correctly escaped the environment variable values when they are defined.

Once the environment variables are defined, Travis CI will run the template validate and build steps as part of the test process.

## Contributing

This project welcomes contributions and suggestions. Our goal is to
work on Azure specific tooling for deploying and managing the open
source [Moodle](http://moodle.org) learning management system on
Azure. We do not work on Moodle itself here, instead we work upstream
as appropriate.

The short version of how to contribute to this project is "just do
it". Where "it" can be defined as any valuable contribution (and to be
clear, asking questions is a valuable contribution):

- ask questions
- provide feedback
- write or update documentation
- help new users
- recommend the project to others
- test the code and report bugs
- fix bugs and issue pull requests
- give us feedback on required features
- write and update the software
- create artwork
- translate to different languages
- anything you can see that needs doing

For a more detailed discussion of how to contribute see our [Contribution Guide](CONTRIBUTE.md).

## Code of Conduct

This project has adopted the [Microsoft Open Source Code of
Conduct](https://opensource.microsoft.com/codeofconduct/). For more
information see the [Code of Conduct
FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact
[opencode@microsoft.com](mailto:opencode@microsoft.com) with any
additional questions or comments.

## Legal Notices

Microsoft and any contributors grant you a license to the Microsoft
documentation and other content in this repository under the [Creative
Commons Attribution 4.0 International Public
License](https://creativecommons.org/licenses/by/4.0/legalcode), see
the [LICENSE](LICENSE) file, and grant you a license to any code in
the repository under the [MIT
License](https://opensource.org/licenses/MIT), see the
[LICENSE-CODE](LICENSE-CODE) file.

Microsoft, Windows, Microsoft Azure and/or other Microsoft products
and services referenced in the documentation may be either trademarks
or registered trademarks of Microsoft in the United States and/or
other countries. The licenses for this project do not grant you rights
to use any Microsoft names, logos, or trademarks. Microsoft's general
trademark guidelines can be found at
[http://go.microsoft.com/fwlink/?LinkID=254653](http://go.microsoft.com/fwlink/?LinkID=254653).

Privacy information can be found at [https://privacy.microsoft.com/en-us/](https://privacy.microsoft.com/en-us/)

Microsoft and any contributors reserve all others rights, whether
under their respective copyrights, patents, or trademarks, whether by
implication, estoppel or otherwise.

## Next Steps

  1. [Deploy a Moodle Cluster](docs/Deploy.md)
  1. [Obtain Deployment Details about a Moodle Cluster](docs/Get-Install-Data.md)
  1. [Delete a Moodle Cluster](docs/Delete.md)


================================================
FILE: SECURITY.md
================================================
<!-- BEGIN MICROSOFT SECURITY.MD V0.0.7 BLOCK -->

## Security

Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/).

If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below.

## Reporting Security Issues

**Please do not report security vulnerabilities through public GitHub issues.**

Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report).

If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com).  If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey).

You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc). 

Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue:

  * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.)
  * Full paths of source file(s) related to the manifestation of the issue
  * The location of the affected source code (tag/branch/commit or direct URL)
  * Any special configuration required to reproduce the issue
  * Step-by-step instructions to reproduce the issue
  * Proof-of-concept or exploit code (if possible)
  * Impact of the issue, including how an attacker might exploit the issue

This information will help us triage your report more quickly.

If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs.

## Preferred Languages

We prefer all communications to be in English.

## Policy

Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd).

<!-- END MICROSOFT SECURITY.MD BLOCK -->


================================================
FILE: azuredeploy-large-ha.json
================================================
{
    "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
        "_artifactsLocation": {
            "type": "string",
            "metadata": {
                "description": "The base URI where artifacts required by this template are located. When the template is deployed using the accompanying scripts, a private location in the subscription will be used and this value will be automatically generated."
            },
            "defaultValue": "https://raw.githubusercontent.com/Azure/Moodle/master/"
        },
        "_artifactsLocationSasToken": {
            "type": "securestring",
            "metadata": {
                "description": "The sasToken required to access _artifactsLocation.  When the template is deployed using the accompanying scripts, a sasToken will be automatically generated."
            },
            "defaultValue": ""
        },
        "sshPublicKey": {
            "metadata": {
                "description": "ssh public key"
            },
            "type": "string"
        },
        "mysqlflexHaEnabled": {
            "allowedValues": [
                "Disabled",
                "SameZone",
                "ZoneRedundant"
            ],
            "defaultValue": "Disabled",
            "metadata": {
                "description": "High availability mode for azure database for mysql flexible server."
            },
            "type": "string"
        }
    },
    "resources": [
        {
            "type": "Microsoft.Resources/deployments",
            "apiVersion": "2017-05-10",
            "name": "mainTemplate",
            "properties": {
                "mode": "Incremental",
                "parameters": {
                    "_artifactsLocation":           { "value": "[parameters('_artifactsLocation')]" },
                    "_artifactsLocationSasToken":   { "value": "[parameters('_artifactsLocationSasToken')]" },
                    "redisDeploySwitch":            { "value": true },
                    "sshPublicKey":                 { "value": "[parameters('sshPublicKey')]" },
                    "autoscaleVmCountMax":          { "value": 20 },
                    "autoscaleVmSku":		        { "value": "Standard_DS3_v2" },
                    "searchType":                   { "value": "elastic" },
                    "dbServerType":                 { "value": "mysqlflex" },
                    "vnetDbDeploySwitch":           { "value": true },
                    "mysqlflexSkuName":             { "value": "Standard_D16ds_v4" },
                    "mysqlflexStgIops":             { "value": 5000 },
                    "mysqlflexStgSizeGiB":          { "value": 512 },
                    "mysqlflexHaEnabled":           { "value": "[parameters('mysqlflexHaEnabled')]" },
                    "fileServerType":               { "value": "azurefiles" },
                    "fileServerDiskSize":           { "value": 1024 },
                    "storageAccountType":           { "value": "Premium_LRS" },
                    "loadBalancerSku":              { "value": "Standard" },
                    "OSDiskSizeInGB":               { "value": 1024 }
                },
                "templateLink": {
                    "uri": "[concat(parameters('_artifactsLocation'), 'azuredeploy.json', parameters('_artifactsLocationSasToken'))]"
                }
            }
        }
    ],
    "outputs": {
        "mainTemplateOutputs": {
            "type": "object",
            "value": "[reference('mainTemplate').outputs]"
        }
    },
    "variables": {
        "documentation01": "This wrapper template calls the main-template with pre-defined configs for large size workloads (with high availability) and the only required parameter (sshPublicKey).",
        "documentation02": "For the expected small-to-mid size workloads, other parameters are fixed in this template and overridden as above."
    }
}


================================================
FILE: azuredeploy-maximal.json
================================================
{
    "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
        "_artifactsLocation": {
            "type": "string",
            "metadata": {
                "description": "The base URI where artifacts required by this template are located. When the template is deployed using the accompanying scripts, a private location in the subscription will be used and this value will be automatically generated."
            },
            "defaultValue": "https://raw.githubusercontent.com/Azure/Moodle/master/"
        },
        "_artifactsLocationSasToken": {
            "type": "securestring",
            "metadata": {
                "description": "The sasToken required to access _artifactsLocation.  When the template is deployed using the accompanying scripts, a sasToken will be automatically generated."
            },
            "defaultValue": ""
        },
        "sshPublicKey": {
            "metadata": {
                "description": "ssh public key"
            },
            "type": "string"
        },
        "mysqlflexHaEnabled": {
            "allowedValues": [
                "Disabled",
                "SameZone",
                "ZoneRedundant"
            ],
            "defaultValue": "Disabled",
            "metadata": {
                "description": "High availability mode for azure database for mysql flexible server."
            },
            "type": "string"
        }
    },
    "resources": [
        {
            "type": "Microsoft.Resources/deployments",
            "apiVersion": "2017-05-10",
            "name": "mainTemplate",
            "properties": {
                "mode": "Incremental",
                "parameters": {
                    "_artifactsLocation":           { "value": "[parameters('_artifactsLocation')]" },
                    "_artifactsLocationSasToken":   { "value": "[parameters('_artifactsLocationSasToken')]" },
                    "redisDeploySwitch":            { "value": true },
                    "azureBackupSwitch":            { "value": true },
                    "searchType":                   { "value": "elastic" },
                    "sshPublicKey":                 { "value": "[parameters('sshPublicKey')]" },
                    "dbServerType":                 { "value": "mysqlflex" },
                    "vnetDbDeploySwitch":           { "value": true },
                    "mysqlflexSkuName":             { "value": "Standard_E64ds_v4" },
                    "mysqlflexStgIops":             { "value": 10000 },
                    "mysqlflexStgSizeGiB":          { "value": 1024 },
                    "mysqlflexHaEnabled":           { "value": "[parameters('mysqlflexHaEnabled')]" },
                    "fileServerType":               { "value": "azurefiles" },
                    "fileServerDiskSize":           { "value": 1024 },
                    "storageAccountType":           { "value": "Premium_LRS" },
                    "autoscaleVmSku":		        { "value": "Standard_DS3_v2" }
                },
                "templateLink": {
                    "uri": "[concat(parameters('_artifactsLocation'), 'azuredeploy.json', parameters('_artifactsLocationSasToken'))]"
                }
            }
        }
    ],
    "outputs": {
        "mainTemplateOutputs": {
            "type": "object",
            "value": "[reference('mainTemplate').outputs]"
        }
    },
    "variables": {
        "documentation01": "This wrapper template calls the main-template with maximum configs and the only required parameter (sshPublicKey).",
        "documentation02": "For the best-possible performance, highly available, and most Moodle features, other parameters are fixed in this template and overridden as above."
    }
}


================================================
FILE: azuredeploy-minimal.json
================================================
{
    "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
        "_artifactsLocation": {
            "type": "string",
            "metadata": {
                "description": "The base URI where artifacts required by this template are located. When the template is deployed using the accompanying scripts, a private location in the subscription will be used and this value will be automatically generated."
            },
            "defaultValue": "https://raw.githubusercontent.com/Azure/Moodle/master/"
        },
        "_artifactsLocationSasToken": {
            "type": "securestring",
            "metadata": {
                "description": "The sasToken required to access _artifactsLocation.  When the template is deployed using the accompanying scripts, a sasToken will be automatically generated."
            },
            "defaultValue": ""
        },
        "sshPublicKey": {
            "metadata": {
                "description": "ssh public key"
            },
            "type": "string"
        }
    },
    "resources": [
        {
            "type": "Microsoft.Resources/deployments",
            "apiVersion": "2021-01-01",
            "name": "mainTemplate",
            "properties": {
                "mode": "Incremental",
                "parameters": {
                    "_artifactsLocation":           { "value": "[parameters('_artifactsLocation')]" },
                    "_artifactsLocationSasToken":   { "value": "[parameters('_artifactsLocationSasToken')]" },
                    "redisDeploySwitch":            { "value": false },
                    "sshPublicKey":                 { "value": "[parameters('sshPublicKey')]" },
                    "autoscaleVmSku":               { "value": "Standard_DS1_v2" },
                    "enableAccelNwForOtherVmsSwitch":   { "value": false },
                    "dbServerType":                 { "value": "mysqlflex" },
                    "vnetDbDeploySwitch":           {"value": true },
                    "fileServerDiskCount":          { "value": 2 },
                    "fileServerDiskSize":           { "value": 32 }
                },
                "templateLink": {
                    "uri": "[concat(parameters('_artifactsLocation'), 'azuredeploy.json', parameters('_artifactsLocationSasToken'))]"
                }
            }
        }
    ],
    "outputs": {
        "mainTemplateOutputs": {
            "type": "object",
            "value": "[reference('mainTemplate').outputs]"
        }
    },
    "variables": {
        "documentation01": "This wrapper template calls the main-template with bare minimum configs and the only required parameter (sshPublicKey).",
        "documentation02": "To speed up deployment and consume least resources, other parameters are fixed in this tempalte and overriden as follows:",
        "documentation03": "   - fileServerType: nfs",
        "documentation04": "   - autoscaleVmSku: Standard_DS1_vs",
        "documentation05": "   - fileServerDiskCount: 2",
        "documentation06": "   - dbServerType: mysqlflex",
        "documentation07": "   - redisDeploySwitch: false"
    }
}


================================================
FILE: azuredeploy-small2mid-noha.json
================================================
{
    "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
        "_artifactsLocation": {
            "type": "string",
            "metadata": {
                "description": "The base URI where artifacts required by this template are located. When the template is deployed using the accompanying scripts, a private location in the subscription will be used and this value will be automatically generated."
            },
            "defaultValue": "https://raw.githubusercontent.com/Azure/Moodle/master/"
        },
        "_artifactsLocationSasToken": {
            "type": "securestring",
            "metadata": {
                "description": "The sasToken required to access _artifactsLocation.  When the template is deployed using the accompanying scripts, a sasToken will be automatically generated."
            },
            "defaultValue": ""
        },
        "sshPublicKey": {
            "metadata": {
                "description": "ssh public key"
            },
            "type": "string"
        }
    },
    "resources": [
        {
            "type": "Microsoft.Resources/deployments",
            "apiVersion": "2017-05-10",
            "name": "mainTemplate",
            "properties": {
                "mode": "Incremental",
                "parameters": {
                    "_artifactsLocation":           { "value": "[parameters('_artifactsLocation')]" },
                    "_artifactsLocationSasToken":   { "value": "[parameters('_artifactsLocationSasToken')]" },
                    "redisDeploySwitch":            { "value": false },
                    "sshPublicKey":                 { "value": "[parameters('sshPublicKey')]" },
                    "dbServerType":                 { "value": "mysqlflex" },
                    "vnetDbDeploySwitch":           {"value": true },
                    "mysqlflexSkuName":             { "value": "Standard_D8ds_v4" },
                    "mysqlflexStgIops":             { "value": 1000 },
                    "mysqlflexStgSizeGiB":          { "value": 128 },
                    "fileServerDiskSize":           { "value": 128 }
                },
                "templateLink": {
                    "uri": "[concat(parameters('_artifactsLocation'), 'azuredeploy.json', parameters('_artifactsLocationSasToken'))]"
                }
            }
        }
    ],
    "outputs": {
        "mainTemplateOutputs": {
            "type": "object",
            "value": "[reference('mainTemplate').outputs]"
        }
    },
    "variables": {
        "documentation01": "This wrapper template calls the main-template with pre-defined configs for small-to-mid size workloads (without high availability) and the only required parameter (sshPublicKey).",
        "documentation02": "For the expected small-to-mid size workloads, other parameters are fixed in this template and overridden as above."
    }
}


================================================
FILE: azuredeploy.json
================================================
{
    "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
        "_artifactsLocation": {
            "type": "string",
            "metadata": {
                "description": "The base URI where artifacts required by this template are located. When the template is deployed using the accompanying scripts, a private location in the subscription will be used and this value will be automatically generated."
            },
            "defaultValue": "https://raw.githubusercontent.com/Azure/Moodle/master/"
        },
        "_artifactsLocationSasToken": {
            "type": "securestring",
            "metadata": {
                "description": "The sasToken required to access _artifactsLocation.  When the template is deployed using the accompanying scripts, a sasToken will be automatically generated."
            },
            "defaultValue": ""
        },
        "applyScriptsSwitch": {
            "defaultValue": true,
            "metadata": {
                "description": "Switch to process or bypass all scripts/extensions"
            },
            "type": "bool"
        },
        "azureBackupSwitch": {
            "defaultValue": false,
            "metadata": {
                "description": "Switch to configure AzureBackup and enlist VM's"
            },
            "type": "bool"
        },
        "redisDeploySwitch": {
            "defaultValue": false,
            "metadata": {
                "description": "Switch to deploy a redis cache or not. Note that certain versions of Moodle (e.g., 3.1) don't work well with Redis, so use this only for known well-working Moodle versions (e.g., 3.4)."
            },
            "type": "bool"
        },
        "vnetGwDeploySwitch": {
            "defaultValue": false,
            "metadata": {
                "description": "Switch to deploy a virtual network gateway or not"
            },
            "type": "bool"
        },
        "installObjectFsSwitch": {
            "defaultValue": false,
            "metadata": {
                "description": "Switch to install Moodle Object FS plugins (with Azure Blob storage)"
            },
            "type": "bool"
        },
        "installO365pluginsSwitch": {
            "defaultValue": false,
            "metadata": {
                "description": "Switch to install Moodle Office 365 plugins. As of May 22, 2018, O365 plugins for Moodle 3.5 haven't been released, so to set this true, you must set the moodleVersion to 3.4 or below."
            },
            "type": "bool"
        },
        "installGdprPluginsSwitch": {
            "defaultValue": false,
            "metadata": {
                "description": "(Should be used only for Moodle 3.4 & 3.3) Switch to install Moodle GDPR plugins. Note these require Moodle versions 3.4.2+ or 3.3.5+ and these are included by default in Moodle 3.5. So if you choose MOODLE_35_STABLE as your moodleVersion, do not set this to true."
            },
            "type": "bool"
        },
        "htmlLocalCopySwitch": {
            "defaultValue": true,
            "metadata": {
                "description": "Switch to create a local copy of /moodle/html or not"
            },
            "type": "bool"
        },
        "ddosSwitch": {
            "defaultValue": false,
            "metadata": {
                "description": "Switch to create a DDoS protection plan"
            },
            "type": "bool"
        },
        "enableAccelNwForCtlrVmSwitch": {
            "defaultValue": false,
            "metadata": {
                "description": "Switch to enable Azure Accelerated Networking on the controller VM. Default to false because currently the default controller VM SKU (D1) doesn't support AN. Change this to true if you set the controller VM SKU to eligibible ones (e.g., D2) for better performance."
            },
            "type": "bool"
        },
        "enableAccelNwForOtherVmsSwitch": {
            "defaultValue": true,
            "metadata": {
                "description": "Switch to enable Azure Accelerated Networking on all other VMs. Default to true because currently the default controller VM SKU for all other VMS (D2) does support AN. Change this to false if you set the SKU of any other VMs to an ineligibible one (e.g., D1) to avoid deployment failure."
            },
            "type": "bool"
        },
        "vnetDbDeploySwitch": {
            "defaultValue": false,
            "metadata": {
                "description": "Switch to deploying the db in VNet. Note: currently the Vnet DB Deployment is available for mysqlflex only. For other db server types, the database will not be deployed in VNet."
            },
            "type": "bool"
        },
        "httpsTermination": {
            "allowedValues": [
                "VMSS",
                "AppGw",
                "None"
            ],
            "defaultValue": "VMSS",
            "metadata": {
                "description": "Indicates where https termination occurs. 'VMSS' is for https termination at the VMSS instance VMs (using nginx https proxy). 'AppGw' is for https termination with an Azure Application Gateway. When selecting this, you need to specify all appGw* parameters. 'None' is for testing only with no https. 'None' may not be used with a separately configured https termination layer. If you want to use the 'None' option with your separately configured https termination layer, you'll need to update your Moodle config.php manually for $cfg->wwwroot and $cfg->sslproxy."
            },
            "type": "string"
        },
        "siteURL": {
            "defaultValue": "www.example.org",
            "metadata": {
                "description": "URL for Moodle site"
            },
            "type": "string"
        },
        "moodleVersion": {
            "allowedValues": [
                "MOODLE_405_STABLE"
            ],
            "defaultValue": "MOODLE_405_STABLE",
            "metadata": {
                "description": "The Moodle version you want to install."
            },
            "type": "string"
        },
        "sshPublicKey": {
            "metadata": {
                "description": "ssh public key"
            },
            "type": "string"
        },
        "sshUsername": {
            "defaultValue": "azureadmin",
            "metadata": {
                "description": "ssh user name"
            },
            "type": "string"
        },
        "controllerVmSku": {
            "defaultValue": "Standard_DS1_v2",
            "metadata": {
                "description": "VM size for the controller VM"
            },
            "type": "string"
        },
        "webServerType": {
            "defaultValue": "nginx",
            "allowedValues": [
                "apache",
                "nginx"
            ],
            "metadata": {
                "description": "Web server type"
            },
            "type": "string"
        },
        "autoscaleVmSku": {
            "defaultValue": "Standard_DS2_v2",
            "metadata": {
                "description": "VM size for autoscaled web VMs"
            },
            "type": "string"
        },
        "autoscaleVmCountMax": {
            "defaultValue": 10,
            "metadata": {
                "description": "Maximum number of autoscaled web VMs"
            },
            "type": "int"
        },
        "autoscaleVmCountMin": {
            "defaultValue": 1,
            "metadata": {
                "description": "Minimum (also initial) number of autoscaled web VMs"
            },
            "type": "int"
        },
        "osDiskStorageType": {
            "defaultValue": "Premium_LRS",
            "allowedValues": [
                "Premium_LRS",
                "Standard_LRS"
            ],
            "metadata": {
                "description": "Azure storage type for all VMs' OS disks. With htmlLocalCopySwith true, Premium_LRS (SSD) is strongly recommended, as PHP files will be served from OS disks."
            },
            "type": "string"
        },
        "phpVersion": {
            "allowedValues": [
                "8.1"
            ],
            "defaultValue": "8.1",
            "metadata": {
                "description": "php version"
            },
            "type": "string"
        },
        "dbServerType": {
            "defaultValue": "mysqlflex",
            "allowedValues": [
                "postgres",
                "mysql",
                "mssql",
                "mysqlflex"
            ],
            "metadata": {
                "description": "Database type"
            },
            "type": "string"
        },
        "dbLogin": {
            "defaultValue": "dbadmin",
            "metadata": {
                "description": "Database admin username"
            },
            "type": "string"
        },
        "moodleDbName": {
            "defaultValue": "moodle",
            "metadata": {
                "description": "Moodle Database name"
            },
            "type": "string"
        },
        "moodleDbUser": {
            "defaultValue": "moodle",
            "metadata": {
                "description": "Moodle Database username. This user is different from Database admin user."
            },
            "type": "string"
        },
        "mysqlPgresVcores": {
            "allowedValues": [
                1,
                2,
                4,
                8,
                16,
                32
            ],
            "defaultValue": 2,
            "metadata": {
                "description": "MySql/Postgresql vCores. For Basic tier, only 1 & 2 are allowed. For GeneralPurpose tier, 2, 4, 8, 16, 32 are allowed. For MemoryOptimized, 2, 4, 8, 16 are allowed."
            },
            "type": "int"
        },
        "mysqlPgresStgSizeGB": {
            "defaultValue": 125,
            "minValue": 5,
            "maxValue": 1024,
            "metadata": {
                "description": "MySql/Postgresql storage size in GB. Minimum 5GB, increase by 1GB, up to 1TB (1024 GB)"
            },
            "type": "int"
        },
        "mysqlPgresSkuTier": {
            "allowedValues": [
                "Basic",
                "GeneralPurpose",
                "MemoryOptimized"
            ],
            "defaultValue": "GeneralPurpose",
            "metadata": {
                "description": "MySql/Postgresql sku tier"
            },
            "type": "string"
        },
        "mysqlPgresSkuHwFamily": {
            "allowedValues": [
                "Gen4",
                "Gen5"
            ],
            "defaultValue": "Gen5",
            "metadata": {
                "description": "MySql/Postgresql sku hardware family. Central US is Gen4 only, so make sure to change this parameter to Gen4 if your deployment is on Central US."
            },
            "type": "string"
        },
        "mysqlVersion": {
            "allowedValues": [
                "8.0.21"
            ],
            "defaultValue": "8.0.21",
            "metadata": {
                "description": "Mysql version"
            },
            "type": "string"
        },
        "mysqlflexSkuName": {
            "allowedValues": [
                "Standard_B1s",
                "Standard_B1ms",
                "Standard_B2s",
                "Standard_B2ms",
                "Standard_B4ms",
                "Standard_B8ms",
                "Standard_B12ms",
                "Standard_B16ms",
                "Standard_B20ms",
                "Standard_D2ads_v5",
                "Standard_D2ds_v4",
                "Standard_D4ads_v5",
                "Standard_D4ds_v4",
                "Standard_D8ads_v5",
                "Standard_D8ds_v4",
                "Standard_D16ads_v5",
                "Standard_D16ds_v4",
                "Standard_D32ads_v5",
                "Standard_D32ds_v4",
                "Standard_D48ads_v5",
                "Standard_D48ds_v4",
                "Standard_D64ads_v5",
                "Standard_D64ds_v4",
                "Standard_E2ds_v4",
                "Standard_E2ads_v5",
                "Standard_E4ds_v4",
                "Standard_E4ads_v5",
                "Standard_E8ds_v4",
                "Standard_E8ads_v5",
                "Standard_E16ds_v4",
                "Standard_E16ads_v5",
                "Standard_E32ds_v4",
                "Standard_E32ads_v5",
                "Standard_E48ds_v4",
                "Standard_E48ads_v5",
                "Standard_E64ds_v4",
                "Standard_E64ads_v5",
                "Standard_E80ids_v4",
                "Standard_E2ds_v5",
                "Standard_E4ds_v5",
                "Standard_E8ds_v5",
                "Standard_E16ds_v5",
                "Standard_E32ds_v5",
                "Standard_E48ds_v5",
                "Standard_E64ds_v5",
                "Standard_E96ds_v5"
            ],
            "defaultValue": "Standard_B2s",
            "metadata": {
                "description": "The name of the sku for Azure Database for MySQL Flexible Servers, e.g. Standard_D32ds_v4. Complete list is available here : https://learn.microsoft.com/en-us/azure/mysql/flexible-server/concepts-service-tiers-storage "
            },
            "type": "string"
        },
        "mysqlflexStgSizeGiB": {
            "defaultValue": 20,
            "minValue": 20,
            "maxValue": 16384,
            "metadata": {
                "description": "Azure Database for MySQL Flexible server storage size in GiB. Minimum 20GiB, increase by 1GiB, up to 16TiB (1024 GiB)"
            },
            "type": "int"
        },
        "mysqlflexStgIops": {
            "defaultValue": 360,
            "minValue": 360,
            "metadata": {
                "description": "Azure Database for MySQL Flexible server storage iops. Minimum 360, Maxium is determined by selected mysqlflexSkuName."
            },
            "type": "int"
        },
        "mysqlflexStgAutogrow": {
            "allowedValues": [
                "Enabled",
                "Disabled"
            ],
            "defaultValue": "Enabled",
            "type": "string"
        },
        "mysqlflexHaEnabled": {
            "allowedValues": [
                "Disabled",
                "SameZone",
                "ZoneRedundant"
            ],
            "defaultValue": "Disabled",
            "metadata": {
              "description": "High availability mode for azure database for mysql flexible server."
            },
            "type": "string"
        },
        "mysqlflexAvailabilityZone": {
            "allowedValues": [
                "1",
                "2",
                "3",
                ""
            ],
            "defaultValue": "",
            "metadata": {
                "description": "Availability Zone information of the server. (Leave blank for No Preference)."
            },
            "type": "string"
        },
        "mysqlflexStandbyAvailabilityZone": {
            "allowedValues": [
                "1",
                "2",
                "3",
                ""
            ],
            "defaultValue": "",
            "metadata": {
            "description": "Availability zone of the High availability standby server. (Leave blank for No Preference). Add this value if HA is enabled."
            },
            "type": "string"
        },
        "postgresVersion": {
            "allowedValues": [
                "9.6"
            ],
            "defaultValue": "9.6",
            "metadata": {
                "description": "Postgresql version"
            },
            "type": "string"
        },
        "sslEnforcement": {
            "allowedValues": [
                "Disabled",
                "Enabled"
            ],
            "defaultValue": "Disabled",
            "metadata": {
                "description": "MySql/Postgresql SSL connection"
            },
            "type": "string"
        },
        "mssqlDbServiceObjectiveName": {
            "allowedValues": [
                "S1",
                "S2",
                "S3",
                "S4",
                "S5",
                "S6",
                "S7",
                "S9"
            ],
            "defaultValue": "S1",
            "metadata": {
                "description": "MS SQL database service object names"
            },
            "type": "string"
        },
        "mssqlDbSize": {
            "allowedValues": [
                "100MB",
                "250MB",
                "500MB",
                "1GB",
                "2GB",
                "5GB",
                "10GB",
                "20GB",
                "30GB",
                "40GB",
                "50GB",
                "100GB",
                "250GB",
                "300GB",
                "400GB",
                "500GB",
                "750GB",
                "1024GB"
            ],
            "defaultValue": "250GB",
            "metadata": {
                "description": "MS SQL database size"
            },
            "type": "string"
        },
        "mssqlDbEdition": {
            "allowedValues": [
                "Basic",
                "Standard"
            ],
            "defaultValue": "Standard",
            "metadata": {
                "description": "MS SQL DB edition"
            },
            "type": "string"
        },
        "mssqlVersion": {
            "allowedValues": [
                "12.0"
            ],
            "defaultValue": "12.0",
            "metadata": {
                "description": "Mssql version"
            },
            "type": "string"
        },
        "fileServerType": {
            "defaultValue": "nfs",
            "allowedValues": [
                "gluster",
                "nfs",
                "nfs-ha",
                "nfs-byo",
                "azurefiles"
            ],
            "metadata": {
                "description": "File server type: GlusterFS, NFS, and NFS-HA (2-VM highly available NFS cluster)"
            },
            "type": "string"
        },
        "nfsByoIpExportPath": {
            "defaultValue": "",
            "metadata": {
                "description": "IP address and export path of the BYO-NFS share when fileServerType == nfs-byo. E.g., 172.16.1.8:/msazure"
            },
            "type": "string"
        },
        "OSDiskSizeInGB": {
            "defaultValue": 256,
            "metadata": {
                "description": "OS disk size per Webserver in VMSS"
            },
            "type": "int"
        },
        "fileServerDiskSize": {
            "defaultValue": 127,
            "metadata": {
                "description": "Size per disk for gluster nodes or nfs server"
            },
            "type": "int"
        },
        "fileServerDiskCount": {
            "defaultValue": 4,
            "minValue": 2,
            "maxValue": 8,
            "metadata": {
                "description": "Number of disks in raid0 per gluster node or nfs server"
            },
            "type": "int"
        },
        "fileServerVmSku": {
            "defaultValue": "Standard_DS2_v2",
            "metadata": {
                "description": "VM size for the gluster or NFS-HA nodes"
            },
            "type": "string"
        },
        "keyVaultResourceId": {
            "defaultValue": "",
            "metadata": {
                "description": "(VMSS https termination only) Azure Resource Manager resource ID of the Key Vault in case you stored your SSL cert in an Azure Key Vault (Note that this Key Vault must have been pre-created on the same Azure region where this template is being deployed). Leave this blank if you didn't. Resource ID example: /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/xxx/providers/Microsoft.KeyVault/vaults/yyy. This value can be obtained from keyvault.sh output if you used the script to store your SSL cert in your Key Vault."
            },
            "type": "string"
        },
        "sslCertKeyVaultURL": {
            "defaultValue": "",
            "metadata": {
                "description": "(VMSS https termination only) Azure Key Vault URL for your stored SSL cert. This value can be obtained from keyvault.sh output if you used the script to store your SSL cert in your Key Vault. This parameter is ignored if the keyVaultResourceId parameter is blank."
            },
            "type": "string"
        },
        "sslCertThumbprint": {
            "defaultValue": "",
            "metadata": {
                "description": "(VMSS https termination only) Thumbprint of your stored SSL cert. This value can be obtained from keyvault.sh output if you used the script to store your SSL cert in your Key Vault. This parameter is ignored if the keyVaultResourceId parameter is blank."
            },
            "type": "string"
        },
        "caCertKeyVaultURL": {
            "defaultValue": "",
            "metadata": {
                "description": "(VMSS https termination only) Azure Key Vault URL for your stored CA (Certificate Authority) cert. This value can be obtained from keyvault.sh output if you used the script to store your CA cert in your Key Vault. This parameter is ignored if the keyVaultResourceId parameter is blank."
            },
            "type": "string"
        },
        "caCertThumbprint": {
            "defaultValue": "",
            "metadata": {
                "description": "(VMSS https termination only) Thumbprint of your stored CA cert. This value can be obtained from keyvault.sh output if you used the script to store your CA cert in your Key Vault. This parameter is ignored if the keyVaultResourceId parameter is blank."
            },
            "type": "string"
        },
        "appGwSslCertKeyVaultResourceId": {
            "defaultValue": "",
            "metadata": {
                "description": "(App Gateway https termination only) Azure Key Vault URL for your stored SSL cert, again for App Gateway https termination case only. (Note that this Key Vault must have been pre-created on the same Azure region where this template is being deployed). Leave this blank if you didn't. Resource ID example: /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/xxx/providers/Microsoft.KeyVault/vaults/yyy."
            },
            "type": "string"
        },
        "appGwSslCertKeyVaultSecretName": {
            "defaultValue": "",
            "metadata": {
                "description": "(App Gateway https termination only) Name of the Azure Key Vault secret that's stored in the previously specified Key Vault as a PFX certificate (with no password) for your site's SSL cert. This secret must be pre-populated in the specified Key Vault with the matching name."
            },
            "type": "string"
        },
        "appGwSkuName": {
            "defaultValue": "Standard_v2",
            "allowedValues": [
                "Standard_Small",
                "Standard_Medium",
                "Standard_Large",
                "Standard_v2",
                "WAF_Medium",
                "WAF_Large",
                "WAF_v2"
            ],
            "metadata": {
                "description": "(App Gateway https termination only) Name of the Applicate Gateway SKU"
            },
            "type": "string"
        },
        "appGwSkuTier": {
            "defaultValue": "Standard_v2",
            "allowedValues": [
                "Standard",
                "Standard_v2",
                "WAF",
                "WAF_v2"
            ],
            "metadata": {
                "description": "(App Gateway https termination only) Tier of the Applicate Gateway"
            },
            "type": "string"
        },
        "appGwSkuCapacity": {
            "defaultValue": 2,
            "maxValue": 10,
            "minValue": 2,
            "metadata": {
                "description": "(App Gateway https termination only) Capacity instance count) of the Applicate Gateway"
            },
            "type": "int"
        },
        "storageAccountType": {
            "defaultValue": "Standard_LRS",
            "allowedValues": [
                "Standard_LRS",
                "Standard_GRS",
                "Standard_ZRS",
                "Premium_LRS"
            ],
            "metadata": {
                "description": "Storage Account type. This storage account is only for the Moodle ObjectFS plugin and/or the (currently disabled) Azure Files file share option"
            },
            "type": "string"
        },
        "searchType": {
            "defaultValue": "none",
            "allowedValues": [
                "none",
                "azure",
                "elastic"
            ],
            "metadata": {
                "description": "options of moodle global search"
            },
            "type": "string"
        },
        "tikaService": {
            "defaultValue": "none",
            "allowedValues": [
                "none",
                "tika"
            ],
            "metadata": {
                "description": "options of enabling tika service for file searching in moodle"
            },
            "type": "string"
        },
        "azureSearchSku": {
            "defaultValue": "basic",
            "allowedValues": [
                "free",
                "basic",
                "standard",
                "standard2",
                "standard3"
            ],
            "metadata": {
                "description": "the search service level you want to create."
            },
            "type": "string"
        },
        "azureSearchReplicaCount": {
            "defaultValue": 3,
            "minValue": 1,
            "maxValue": 12,
            "metadata": {
                "description": "Replicas distribute search workloads across the service. You need 2 or more to support high availability (applies to Basic and Standard only)."
            },
            "type": "int"
        },
        "azureSearchPartitionCount": {
            "defaultValue": 1,
            "allowedValues": [
                1,
                2,
                3,
                4,
                6,
                12
            ],
            "metadata": {
                "description": "Partitions allow for scaling of document count as well as faster indexing by sharding your index over multiple Azure Search units."
            },
            "type": "int"
        },
        "azureSearchHostingMode": {
            "defaultValue": "default",
            "allowedValues": [
                "default",
                "highDensity"
            ],
            "metadata": {
                "description": "Applicable only for azureSearchSku set to standard3. You can set this property to enable a single, high density partition that allows up to 1000 indexes, which is much higher than the maximum indexes allowed for any other azureSearchSku."
            },
            "type": "string"
        },
        "elasticVmSku": {
            "defaultValue": "Standard_DS2_v2",
            "metadata": {
                "description": "VM size for the elastic search nodes"
            },
            "type": "string"
        },
        "tikaVmSku": {
            "defaultValue": "Standard_DS2_v2",
            "metadata": {
                "description": "VM size for the tika search nodes"
            },
            "type": "string"
        },
        "customVnetId": {
            "defaultValue": "",
            "metadata": {
                "description": "Azure Resource ID of the Azure virtual network where you want to deploy your Moodle resources. A vnet resource ID is of the following format: /subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxxxxx/resourceGroups/gggg/providers/Microsoft.Network/virtualNetworks/vvvv. Note that this virtual network must be on the same Azure location as this template deployment location. If this parameter is blank, a new Azure virtual network will be created and used. In that case, the address space of the newly created virtual network will be */16 of the following vNetAddressSpace parameter value below."
            },
            "type": "string"
        },
        "vNetAddressSpace": {
            "defaultValue": "172.31.0.0",
            "metadata": {
                "description": "Address range for the Moodle virtual network and various subnets - presumed /16 for a newly created vnet in case customVnetId is blank. Further subneting (a number of */24 subnets starting from the xxx.yyy.zzz.0/24 will be created on a newly created vnet or your BYO-vnet (specified in customVnetId parameter)."
            },
            "type": "string"
        },
        "gatewayType": {
            "allowedValues": [
                "Vpn",
                "ER"
            ],
            "defaultValue": "Vpn",
            "metadata": {
                "description": "Virtual network gateway type"
            },
            "type": "string"
        },
        "vpnType": {
            "allowedValues": [
                "RouteBased",
                "PolicyBased"
            ],
            "defaultValue": "RouteBased",
            "metadata": {
                "description": "Virtual network gateway vpn type"
            },
            "type": "string"
        },
        "loadBalancerSku": {
            "defaultValue": "Standard",
            "allowedValues": [
                "Basic",
                "Standard"
            ],
            "metadata": {
                "description": "Loadbalancer SKU"
            },
            "type": "string"
        },
        "loadBalancerTier": {
            "defaultValue": "Regional",
            "allowedValues": [
                "Regional",
                "Global"
            ],
            "metadata": {
                "description": "Loadbalancer Tier"
            },
            "type": "string"
        },
        "ubuntuVersion": {
            "type": "string",
            "allowedValues": [
                "22_04-lts-gen2"
            ],
            "defaultValue": "22_04-lts-gen2"
        },
        "location": {
            "type": "string",
            "defaultValue": "[resourceGroup().location]",
            "metadata": {
                "description": "Azure Location for all resources."
            }
        },
        "isMigration": {
            "type": "bool",
            "defaultValue": false,
            "metadata": {
                "description": "Indicates whether the this template is using for migration scenario."
            }
        }
    },
    "resources": [
        {
            "type": "Microsoft.Resources/deployments",
            "apiVersion": "2017-05-10",
            "name": "pid-738e3eec-68d4-4667-8377-c05c77c21f1b",
            "properties": {
                "mode": "Incremental",
                "template": {
                    "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
                    "contentVersion": "1.0.0.0",
                    "resources": []
                }
            }
        },
        {
            "type": "Microsoft.Resources/deployments",
            "apiVersion": "2017-05-10",
            "name": "networkTemplate",
            "properties": {
                "mode": "Incremental",
                "parameters": {
                    "moodleCommon": {
                        "value": "[variables('moodleCommon')]"
                    }
                },
                "templateLink": {
                    "uri": "[concat(variables('moodleCommon').baseTemplateUrl,'network.json',parameters('_artifactsLocationSasToken'))]"
                }
            }
        },
        {
            "type": "Microsoft.Resources/deployments",
            "apiVersion": "2017-05-10",
            "name": "storageAccountTemplate",
            "properties": {
                "mode": "Incremental",
                "parameters": {
                    "moodleCommon": {
                        "value": "[variables('moodleCommon')]"
                    }
                },
                "templateLink": {
                    "uri": "[concat(variables('moodleCommon').baseTemplateUrl,'storageAccount.json',parameters('_artifactsLocationSasToken'))]"
                }
            }
        },
        {
            "type": "Microsoft.Resources/deployments",
            "apiVersion": "2017-05-10",
            "dependsOn": [
                "Microsoft.Resources/deployments/networkTemplate"
            ],
            "name": "dbTemplate",
            "properties": {
                "mode": "Incremental",
                "parameters": {
                    "moodleCommon": {
                        "value": "[variables('moodleCommon')]"
                    },
                    "lbPubIp": {
                        "value": "[reference('networkTemplate').outputs.lbPubIp.value]"
                    },
                    "lbOut001PubIp": {
                        "value": "[reference('networkTemplate').outputs.lbOut001PubIp.value]"
                    },
                    "lbOut002PubIp": {
                        "value": "[reference('networkTemplate').outputs.lbOut002PubIp.value]"
                    },
                    "ctlrPubIp": {
                        "value": "[reference('networkTemplate').outputs.ctlrPubIp.value]"
                    },
                    "subnetIdDb": {
                        "value": "[reference('networkTemplate').outputs.subnetIdDb.value]"
                    }
                },
                "templateLink": {
                    "uri": "[concat(variables('moodleCommon').baseTemplateUrl, 'db-', parameters('dbServerType'), '.json', parameters('_artifactsLocationSasToken'))]"
                }
            }
        },
        {
            "condition": "[parameters('azureBackupSwitch')]",
            "type": "Microsoft.Resources/deployments",
            "apiVersion": "2017-05-10",
            "name": "recoveryTemplate",
            "properties": {
                "mode": "Incremental",
                "parameters": {
                    "moodleCommon": {
                        "value": "[variables('moodleCommon')]"
                    }
                },
                "templateLink": {
                    "uri": "[concat(variables('moodleCommon').baseTemplateUrl,'recoveryservices.json',parameters('_artifactsLocationSasToken'))]"
                }
            }
        },
        {
            "condition": "[parameters('redisDeploySwitch')]",
            "type": "Microsoft.Resources/deployments",
            "apiVersion": "2017-05-10",
            "name": "redisTemplate",
            "dependsOn": [
                "Microsoft.Resources/deployments/networkTemplate"
            ],
            "properties": {
                "mode": "Incremental",
                "parameters": {
                    "moodleCommon": {
                        "value": "[variables('moodleCommon')]"
                    },
                    "subnetIdRedis": {
                        "value": "[reference('networkTemplate').outputs.subnetIdRedis.value]"
                    }
                },
                "templateLink": {
                    "uri": "[concat(variables('moodleCommon').baseTemplateUrl, 'redis.json', parameters('_artifactsLocationSasToken'))]"
                }
            }
        },
        {
            "condition": "[not(equals(parameters('searchType'), 'none'))]",
            "type": "Microsoft.Resources/deployments",
            "apiVersion": "2017-05-10",
            "dependsOn": [
                "Microsoft.Resources/deployments/networkTemplate",
                "Microsoft.Resources/deployments/recoveryTemplate"
            ],
            "name": "searchTemplate",
            "properties": {
                "mode": "Incremental",
                "parameters": {
                    "moodleCommon": {
                        "value": "[variables('moodleCommon')]"
                    },
                    "subnetIdElastic": {
                        "value": "[reference('networkTemplate').outputs.subnetIdElastic.value]"
                    }
                },
                "templateLink": {
                    "uri": "[concat(variables('moodleCommon').baseTemplateUrl, 'search-', parameters('searchType'), '.json', parameters('_artifactsLocationSasToken'))]"
                }
            }
        },
        {
            "condition": "[and(equals(parameters('tikaService'), 'tika'), not(equals(parameters('searchType'), 'none')))]",
            "type": "Microsoft.Resources/deployments",
            "apiVersion": "2017-05-10",
            "dependsOn": [
                "Microsoft.Resources/deployments/networkTemplate",
                "Microsoft.Resources/deployments/recoveryTemplate"
            ],
            "name": "tikaTemplate",
            "properties": {
                "mode": "Incremental",
                "parameters": {
                    "moodleCommon": {
                        "value": "[variables('moodleCommon')]"
                    },
                    "subnetIdTika": {
                        "value": "[reference('networkTemplate').outputs.subnetIdTika.value]"
                    }
                },
                "templateLink": {
                    "uri": "[concat(variables('moodleCommon').baseTemplateUrl, 'tika.json', parameters('_artifactsLocationSasToken'))]"
                }
            }
        },
        {
            "condition": "[equals(parameters('fileServerType'),'gluster')]",
            "type": "Microsoft.Resources/deployments",
            "apiVersion": "2017-05-10",
            "dependsOn": [
                "Microsoft.Resources/deployments/networkTemplate",
                "Microsoft.Resources/deployments/recoveryTemplate"
            ],
            "name": "glusterTemplate",
            "properties": {
                "mode": "Incremental",
                "parameters": {
                    "moodleCommon": {
                        "value": "[variables('moodleCommon')]"
                    },
                    "subnetIdSan": {
                        "value": "[reference('networkTemplate').outputs.subnetIdSan.value]"
                    }
                },
                "templateLink": {
                    "uri": "[concat(variables('moodleCommon').baseTemplateUrl,'gluster.json',parameters('_artifactsLocationSasToken'))]"
                }
            }
        },
        {
            "condition": "[equals(parameters('fileServerType'),'nfs-ha')]",
            "type": "Microsoft.Resources/deployments",
            "apiVersion": "2017-05-10",
            "dependsOn": [
                "Microsoft.Resources/deployments/networkTemplate",
                "Microsoft.Resources/deployments/recoveryTemplate"
            ],
            "name": "nfsHaTemplate",
            "properties": {
                "mode": "Incremental",
                "parameters": {
                    "_artifactsLocation": {
                        "value": "[parameters('_artifactsLocation')]"
                    },
                    "_artifactsLocationSasToken": {
                        "value": "[parameters('_artifactsLocationSasToken')]"
                    },
                    "location": {
                        "value": "[parameters('location')]"
                    },
                    "subnetId": {
                        "value": "[reference('networkTemplate').outputs.subnetIdSan.value]"
                    },
                    "node0IPAddr": {
                        "value": "[variables('moodleCommon').nfsHaNode0IP]"
                    },
                    "node1IPAddr": {
                        "value": "[variables('moodleCommon').nfsHaNode1IP]"
                    },
                    "nfsClientsIPRange": {
                        "value": "[variables('moodleCommon').nfsHaClientsIPRange]"
                    },
                    "lbFrontEndIpAddr": {
                        "value": "[variables('moodleCommon').nfsHaLbIP]"
                    },
                    "enableAccelNwSwitch": {
                        "value": "[parameters('enableAccelNwForOtherVmsSwitch')]"
                    },
                    "vmSku": {
                        "value": "[variables('moodleCommon').fileServerVmSku]"
                    },
                    "adminUserName": {
                        "value": "[parameters('sshUsername')]"
                    },
                    "sshPublicKey": {
                        "value": "[parameters('sshPublicKey')]"
                    },
                    "osType": {
                        "value": "[variables('moodleCommon').osType]"
                    },
                    "osDiskStorageType": {
                        "value": "[parameters('osDiskStorageType')]"
                    },
                    "dataDiskCountPerVM": {
                        "value": "[parameters('fileServerDiskCount')]"
                    },
                    "dataDiskSizeInGB": {
                        "value": "[parameters('fileServerDiskSize')]"
                    },
                    "OSDiskSizeInGB": {
                        "value": "[parameters('OSDiskSizeInGB')]"
                    },
                    "resourcesUniqueString": {
                        "value": "[variables('resourceprefix')]"
                    }
                },
                "templateLink": {
                    "uri": "[concat(variables('moodleCommon').baseTemplateUrl, 'nfs-ha.json', parameters('_artifactsLocationSasToken'))]"
                }
            }
        },
        {
            "type": "Microsoft.Resources/deployments",
            "apiVersion": "2017-05-10",
            "dependsOn": [
                "Microsoft.Resources/deployments/networkTemplate",
                "Microsoft.Resources/deployments/dbTemplate",
                "Microsoft.Resources/deployments/redisTemplate",
                "Microsoft.Resources/deployments/searchTemplate",
                "Microsoft.Resources/deployments/storageAccountTemplate"
            ],
            "name": "vmSetupParamsTemplate",
            "properties": {
                "mode": "Incremental",
                "parameters": {
                    "moodleCommon": {
                        "value": "[variables('moodleCommon')]"
                    },
                    "dbFQDN": {
                        "value": "[reference('dbTemplate').outputs.dbFQDN.value]"
                    },
                    "storageAccountName": {
                        "value": "[reference('storageAccountTemplate').outputs.storageAccountName.value]"
                    },
                    "storageAccountKey": {
                        "value": "[reference('storageAccountTemplate').outputs.storageAccountKey.value]"
                    },
                    "redisKey": {
                        "value": "[if(parameters('redisDeploySwitch'), reference('redisTemplate').outputs.redisKey.value, 'None')]"
                    },
                    "azureSearchKey": {
                        "value": "[if(equals(parameters('searchType'), 'azure'), reference('searchTemplate').outputs.azureSearchKey.value, 'None')]"
                    }
                },
                "templateLink": {
                    "uri": "[concat(variables('moodleCommon').baseTemplateUrl,'vmsetupparams.json',parameters('_artifactsLocationSasToken'))]"
                }
            }
        },
        {
            "type": "Microsoft.Resources/deployments",
            "apiVersion": "2017-05-10",
            "dependsOn": [
                "Microsoft.Resources/deployments/vmSetupParamsTemplate",
                "Microsoft.Resources/deployments/glusterTemplate",
                "Microsoft.Resources/deployments/nfsHaTemplate",
                "Microsoft.Resources/deployments/recoveryTemplate",
                "Microsoft.Resources/deployments/networkTemplate",
                "Microsoft.Resources/deployments/dbTemplate",
                "Microsoft.Resources/deployments/redisTemplate",
                "Microsoft.Resources/deployments/searchTemplate",
                "Microsoft.Resources/deployments/storageAccountTemplate"
            ],
            "name": "controllerTemplate",
            "properties": {
                "mode": "Incremental",
                "parameters": {
                    "moodleCommon": {
                        "value": "[variables('moodleCommon')]"
                    },
                    "subnetIdWeb": {
                        "value": "[reference('networkTemplate').outputs.subnetIdWeb.value]"
                    },
                    "ctlrPubIpId": {
                        "value": "[reference('networkTemplate').outputs.ctlrPubIpId.value]"
                    },
                    "vmSetupParamsObj": {
                        "value": "[reference('vmSetupParamsTemplate').outputs.vmSetupParamsObj.value]"
                    }
                },
                "templateLink": {
                    "uri": "[concat(variables('moodleCommon').baseTemplateUrl,'controller.json',parameters('_artifactsLocationSasToken'))]"
                }
            }
        },
        {
            "type": "Microsoft.Resources/deployments",
            "apiVersion": "2017-05-10",
            "dependsOn": [
                "Microsoft.Resources/deployments/vmSetupParamsTemplate",
                "Microsoft.Resources/deployments/controllerTemplate",
                "Microsoft.Resources/deployments/networkTemplate",
                "Microsoft.Resources/deployments/redisTemplate",
                "Microsoft.Resources/deployments/dbTemplate"
            ],
            "name": "scaleSetTemplate",
            "properties": {
                "mode": "Incremental",
                "parameters": {
                    "moodleCommon": {
                        "value": "[variables('moodleCommon')]"
                    },
                    "subnetIdWeb": {
                        "value": "[reference('networkTemplate').outputs.subnetIdWeb.value]"
                    },
                    "vmSetupParamsObj": {
                        "value": "[reference('vmSetupParamsTemplate').outputs.vmSetupParamsObj.value]"
                    }
                },
                "templateLink": {
                    "uri": "[concat(variables('moodleCommon').baseTemplateUrl,'webvmss.json',parameters('_artifactsLocationSasToken'))]"
                }
            }
        }
    ],
    "outputs": {
        "siteURL": {
            "type": "string",
            "value": "[variables('moodleCommon').siteURL]"
        },
        "controllerInstanceIP": {
            "type": "string",
            "value": "[reference('controllerTemplate').outputs.controllerIP.value]"
        },
        "databaseDNS": {
            "type": "string",
            "value": "[reference('dbTemplate').outputs.dbFQDN.value]"
        },
        "databaseAdminUsername": {
            "type": "string",
            "value": "[variables('moodleCommon').dbUsername]"
        },
        "databaseAdminPassword": {
            "type": "string",
            "value": "[variables('moodleCommon').dbLoginPassword]"
        },
        "firstFrontendVmIP": {
            "type": "string",
            "value": "[reference('scaleSetTemplate').outputs.webvm1IP.value]"
        },
        "moodleAdminPassword": {
            "type": "string",
            "value": "[variables('moodleCommon').moodleAdminPass]"
        },
        "moodleDbUsername": {
            "type": "string",
            "value": "[variables('moodleCommon').moodleDbUserAzure]"
        },
        "moodleDbPassword": {
            "type": "string",
            "value": "[variables('moodleCommon').moodleDbPass]"
        },
        "loadBalancerDNS": {
            "type": "string",
            "value": "[variables('moodleCommon').lbDns]"
        },
        "loadBalancerSku": {
            "type": "string",
            "value": "[variables('moodleCommon').lbSku]"
        },
        "loadBalancerTier": {
            "type": "string",
            "value": "[variables('moodleCommon').lbTier]"
        }
    },
    "variables": {
        "documentation01": "This main-template calls multiple sub-templates to create the moodle system",
        "documentation02": "    recoveryservices0   - dummy template (see next statement)",
        "documentation03": "    recoveryservices1   - creates a recovery vault that will be subsequently used by the VM Backup - a paramter swtich controls whethe is is called or bypassed",
        "documentation04": "    redis               - creates a redis cache",
        "documentation05": "    postgres / mysql  - creates a postgresql / mysql server",
        "documentation06": "    vnet                - creates a virtual network with three subnets",
        "documentation07": "    elastic             - creates a elastic search cluster on a vm farm",
        "documentation08": "    gluster             - creates a gluster file system on a vm farm",
        "documentation09": "    webvmss             - creates a vm scale set",
        "documentation10": "    controller          - creates a controller VM and deploys code",
        "documentation11": "GlusterFS Sizing guidance",
        "moodleCommon": {
            "baseTemplateUrl": "[concat(parameters('_artifactsLocation'), 'nested/')]",
            "scriptLocation": "[concat(parameters('_artifactsLocation'), 'scripts/')]",
            "artifactsSasToken": "[parameters('_artifactsLocationSasToken')]",
            "appGwBePoolName": "[concat('appgw-bepool-', variables('resourceprefix'))]",
            "appGwName": "[concat('appgw-', variables('resourceprefix'))]",
            "appGwPipName": "[concat('appgw-pubip-',variables('resourceprefix'))]",
            "appGwSslCertKeyVaultResourceId": "[parameters('appGwSslCertKeyVaultResourceId')]",
            "appGwSslCertKeyVaultSecretName": "[parameters('appGwSslCertKeyVaultSecretName')]",
            "appGwSkuCapacity": "[parameters('appGwSkuCapacity')]",
            "appGwSkuName": "[parameters('appGwSkuName')]",
            "appGwSkuTier": "[parameters('appGwSkuTier')]",
            "applyScriptsSwitch": "[parameters('applyScriptsSwitch')]",
            "autoscaleVmCountMax": "[parameters('autoscaleVmCountMax')]",
            "autoscaleVmCountMin": "[parameters('autoscaleVmCountMin')]",
            "autoscaleVmSku": "[parameters('autoscaleVmSku')]",
            "azureBackupSwitch": "[parameters('azureBackupSwitch')]",
            "azureSearchHostingMode": "[parameters('azureSearchHostingMode')]",
            "azureSearchName": "[concat('azure-search-',variables('resourceprefix'))]",
            "azureSearchNameHost": "[concat('azure-search-',variables('resourceprefix'),'.search.windows.net')]",
            "azureSearchPartitionCount": "[parameters('azureSearchPartitionCount')]",
            "azureSearchReplicaCount": "[parameters('azureSearchReplicaCount')]",
            "azureSearchSku": "[parameters('azureSearchSku')]",
            "commonFunctionsScriptUri": "[concat(parameters('_artifactsLocation'),'scripts/helper_functions.sh',parameters('_artifactsLocationSasToken'))]",
            "controllerVmSku": "[parameters('controllerVmSku')]",
            "customVnetId": "[parameters('customVnetId')]",
            "ctlrNicName": "[concat('controller-vm-nic-',variables('resourceprefix'))]",
            "ctlrNsgName": "[concat('controller-nsg-',variables('resourceprefix'))]",
            "ctlrPipName": "[concat('controller-pubip-',variables('resourceprefix'))]",
            "ctlrVmName": "[concat('controller-vm-',variables('resourceprefix'))]",
            "vmssNsgName": "[concat('vmss-nsg-',variables('resourceprefix'))]",
            "ctlrVmSecrets": "[take(variables('ctlrVmSecretsArray'), if(empty(parameters('keyVaultResourceId')), 0, 1))]",
            "dbLogin": "[parameters('dbLogin')]",
            "dbLoginPassword": "[concat(substring(uniqueString(resourceGroup().id, deployment().name), 2, 11), '*7', toUpper('pfiwb'))]",
            "dbServerType": "[parameters('dbServerType')]",
            "dbUsername": "[if(equals(parameters('dbServerType'), 'mysqlflex'), parameters('dbLogin'), concat(parameters('dbLogin'), '@', parameters('dbServerType'), '-', variables('resourceprefix')))]",
            "ddosPlanName": "[concat('ddos-plan-',variables('resourceprefix'))]",
            "ddosSwitch": "[parameters('ddosSwitch')]",
            "elasticVmSku": "[parameters('elasticVmSku')]",
            "elasticAvailabilitySetName": "[concat('elastic-avset-',variables('resourceprefix'))]",
            "elasticClusterName": "[concat('es-cluster-',variables('resourceprefix'))]",
            "elasticNicName1": "[concat('elastic-vm-nic-01-',variables('resourceprefix'))]",
            "elasticNicName2": "[concat('elastic-vm-nic-02-',variables('resourceprefix'))]",
            "elasticNicName3": "[concat('elastic-vm-nic-03-',variables('resourceprefix'))]",
            "elasticScriptFilename": "install_elastic.sh",
            "elasticVm1IP": "[concat( variables('octets')[0], '.', variables('octets')[1], '.', string(add(int(variables('octets')[2]),5)), '.20')]",
            "elasticVm2IP": "[concat( variables('octets')[0], '.', variables('octets')[1], '.', string(add(int(variables('octets')[2]),5)), '.21')]",
            "elasticVm3IP": "[concat( variables('octets')[0], '.', variables('octets')[1], '.', string(add(int(variables('octets')[2]),5)), '.22')]",
            "elasticVmName": "[concat('elastic-vm-',variables('resourceprefix'))]",
            "elasticVmName1": "[concat('elastic-vm-01-',variables('resourceprefix'))]",
            "elasticVmName2": "[concat('elastic-vm-02-',variables('resourceprefix'))]",
            "elasticVmName3": "[concat('elastic-vm-03-',variables('resourceprefix'))]",
            "enableAccelNwForCtlrVmSwitch": "[parameters('enableAccelNwForCtlrVmSwitch')]",
            "enableAccelNwForOtherVmsSwitch": "[parameters('enableAccelNwForOtherVmsSwitch')]",
            "extBeName": "[concat('lb-backend-',variables('resourceprefix'))]",
            "extFeName": "[concat('lb-frontend-',variables('resourceprefix'))]",
            "extOutName001": "[concat('lb-outbound001-',variables('resourceprefix'))]",
            "extOutName002": "[concat('lb-outbound002-',variables('resourceprefix'))]",
            "extNatPool": "[concat('lb-natpool-',variables('resourceprefix'))]",
            "extProbeHTTP": "[concat('lb-probe-http-',variables('resourceprefix'))]",
            "extProbeHTTPS": "[concat('lb-probe-https-',variables('resourceprefix'))]",
            "fileServerDiskCount": "[parameters('fileServerDiskCount')]",
            "fileServerDiskSize": "[parameters('fileServerDiskSize')]",
            "OSDiskSizeInGB": "[parameters('OSDiskSizeInGB')]",
            "fileServerType": "[parameters('fileServerType')]",
            "fileServerVmCount": 2,
            "fileServerVmSku": "[parameters('fileServerVmSku')]",
            "gatewayName": "[concat('vnet-gateway-',variables('resourceprefix'))]",
            "gatewayPublicIPName": "[concat('vnet-gw-ip-',variables('resourceprefix'))]",
            "gatewayType": "[parameters('gatewayType')]",
            "gfsNameRoot": "[concat('gluster-vm-',variables('resourceprefix'))]",
            "gfxAvailabilitySetName": "[concat('gluster-avset-',variables('resourceprefix'))]",
            "glusterScriptFilename": "install_gluster.sh",
            "htmlLocalCopySwitch": "[parameters('htmlLocalCopySwitch')]",
            "httpsTermination": "[parameters('httpsTermination')]",
            "installGdprPluginsSwitch": "[parameters('installGdprPluginsSwitch')]",
            "installO365pluginsSwitch": "[parameters('installO365pluginsSwitch')]",
            "installObjectFsSwitch": "[parameters('installObjectFsSwitch')]",
            "lbDns": "[concat('lb-',variables('resourceprefix'),'.',parameters('location'),'.cloudapp.azure.com')]",
            "lbSku": "[parameters('loadBalancerSku')]",
            "lbTier": "[parameters('loadBalancerTier')]",
            "lbName": "[concat('lb-',variables('resourceprefix'))]",
            "lbOutName001": "[concat('lb-out001-',variables('resourceprefix'))]",
            "lbOutName002": "[concat('lb-out002-',variables('resourceprefix'))]",
            "lbPipName": "[concat('lb-pubip-',variables('resourceprefix'))]",
            "lbOutPipName001": "[concat('lb-outpubip001-',variables('resourceprefix'))]",
            "lbOutPipName002": "[concat('lb-outpubip002-',variables('resourceprefix'))]",
            "location": "[parameters('location')]",
            "moodleAdminPass": "[concat(toUpper('xl'), substring(uniqueString(resourceGroup().id, deployment().name), 6, 7),',1*8')]",
            "moodleDbName": "[parameters('moodleDbName')]",
            "moodleDbPass": "[concat('9#36^', substring(uniqueString(resourceGroup().id, deployment().name), 5, 8), toUpper('ercq'))]",
            "moodleDbUser": "[parameters('moodleDbUser')]",
            "moodleDbUserAzure": "[if(equals(parameters('dbServerType'), 'mysqlflex'), parameters('moodleDbUser'), concat(parameters('moodleDbUser'), '@', parameters('dbServerType'), '-', variables('resourceprefix')))]",
            "moodleInstallScriptFilename": "install_moodle.sh",
            "moodleOnAzureConfigsJsonPath": "/var/lib/cloud/instance/moodle_on_azure_configs.json",
            "moodleVersion": "[parameters('moodleVersion')]",
            "mssqlDbServiceObjectiveName": "[parameters('mssqlDbServiceObjectiveName')]",
            "mssqlDbSize": "[parameters('mssqlDbSize')]",
            "mssqlDbEdition": "[parameters('mssqlDbEdition')]",
            "mssqlVersion": "[parameters('mssqlVersion')]",
            "mysqlPgresSkuHwFamily": "[parameters('mysqlPgresSkuHwFamily')]",
            "mysqlPgresSkuName": "[concat(if(equals(parameters('mysqlPgresSkuTier'),'Basic'),'B', if(equals(parameters('mysqlPgresSkuTier'),'GeneralPurpose'),'GP', 'MO')), '_', parameters('mysqlPgresSkuHwFamily'), '_', string(parameters('mysqlPgresVcores')))]",
            "mysqlPgresSkuTier": "[parameters('mysqlPgresSkuTier')]",
            "mysqlPgresStgSizeGB": "[parameters('mysqlPgresStgSizeGB')]",
            "mysqlPgresVcores": "[parameters('mysqlPgresVcores')]",
            "mysqlVersion": "[parameters('mysqlVersion')]",
            "mysqlflexSkuName": "[parameters('mysqlflexSkuName')]",
            "mysqlflexSkuTier": "[if(startsWith(parameters('mysqlflexSkuName'), 'Standard_B'), 'Burstable', if(startsWith(parameters('mysqlflexSkuName'), 'Standard_D'), 'GeneralPurpose', 'MemoryOptimized'))]",
            "mysqlflexStgSizeGiB": "[parameters('mysqlflexStgSizeGiB')]",
            "mysqlflexStgIops": "[parameters('mysqlflexStgIops')]",
            "mysqlflexStgAutogrow": "[parameters('mysqlflexStgAutogrow')]",
            "mysqlflexHaEnabled": "[parameters('mysqlflexHaEnabled')]",
            "mysqlflexAvailabilityZone": "[parameters('mysqlflexAvailabilityZone')]",
            "mysqlflexStandbyAvailabilityZone": "[parameters('mysqlflexStandbyAvailabilityZone')]",
            "mysqlflexRequireSecureTransport":  "[if(equals(parameters('sslEnforcement'),'Enabled'),'ON','OFF')]",
            "mysqlflexPrivateDnsZoneName": "[concat(parameters('dbServerType'), '-',variables('resourceprefix'),'.private.mysql.database.azure.com')]",
            "nfsByoIpExportPath": "[parameters('nfsByoIpExportPath')]",
            "nfsHaClientsIPRange": "[variables('subnetWebRange')]",
            "nfsHaExportPath": "/drbd/data",
            "nfsHaLbIP": "[concat(variables('subnetSanPrefix'), '.100')]",
            "nfsHaNode0IP": "[concat(variables('subnetSanPrefix'), '.110')]",
            "nfsHaNode1IP": "[concat(variables('subnetSanPrefix'), '.120')]",
            "osDiskStorageType": "[parameters('osDiskStorageType')]",
            "osType": {
                "offer": "0001-com-ubuntu-server-jammy",
                "publisher": "Canonical",
                "sku": "[parameters('ubuntuVersion')]",
                "version": "latest"
            },
            "phpVersion": "[parameters('phpVersion')]",
            "policyName": "[concat('policy-',variables('resourceprefix'))]",
            "postgresVersion": "[parameters('postgresVersion')]",
            "redisCacheName": "[concat('redis-',variables('resourceprefix'))]",
            "redisDeploySwitch": "[parameters('redisDeploySwitch')]",
            "redisDns": "[concat('redis-',variables('resourceprefix'),'.redis.cache.windows.net')]",
            "resourcesPrefix": "[variables('resourceprefix')]",
            "searchType": "[parameters('searchType')]",
            "serverName": "[concat(parameters('dbServerType'), '-',variables('resourceprefix'))]",
            "siteURL": "[if(or(empty(parameters('siteURL')), equals(parameters('siteURL'), 'www.example.org')), concat(if(equals(parameters('httpsTermination'), 'AppGw'),'appgw-','lb-'),variables('resourceprefix'),'.',parameters('location'),'.cloudapp.azure.com'), parameters('siteURL'))]",
            "sshPublicKey": "[parameters('sshPublicKey')]",
            "sshUsername": "[parameters('sshUsername')]",
            "sslEnforcement": "[parameters('sslEnforcement')]",
            "storageAccountName": "[tolower(concat('abs',variables('resourceprefix')))]",
            "storageAccountType": "[parameters('storageAccountType')]",
            "subnetDb": "[concat('db-subnet-',variables('resourceprefix'))]",
            "subnetDbPrefix": "[concat(variables('octets')[0], '.', variables('octets')[1], '.', string(add(int(variables('octets')[2]),7)))]",
            "subnetDbRange": "[concat(variables('octets')[0], '.', variables('octets')[1], '.', string(add(int(variables('octets')[2]),7)), '.0/24')]",
            "subnetAppGw": "[concat('appgw-subnet-',variables('resourceprefix'))]",
            "subnetAppGwPrefix": "[concat(variables('octets')[0], '.', variables('octets')[1], '.', string(add(int(variables('octets')[2]),6)))]",
            "subnetAppGwRange": "[concat(variables('octets')[0], '.', variables('octets')[1], '.', string(add(int(variables('octets')[2]),6)), '.0/24')]",
            "subnetElastic": "[concat('elastic-subnet-',variables('resourceprefix'))]",
            "subnetElasticPrefix": "[concat( variables('octets')[0], '.', variables('octets')[1], '.', string(add(int(variables('octets')[2]),5)))]",
            "subnetElasticRange": "[concat( variables('octets')[0], '.', variables('octets')[1], '.', string(add(int(variables('octets')[2]),5)), '.0/24')]",
            "subnetGateway": "GatewaySubnet",
            "subnetGatewayPrefix": "[concat(variables('octets')[0], '.', variables('octets')[1], '.', string(add(int(variables('octets')[2]),4)))]",
            "subnetGatewayRange": "[concat(variables('octets')[0], '.', variables('octets')[1], '.', string(add(int(variables('octets')[2]),4)), '.0/24')]",
            "subnetRedis": "[concat('redis-subnet-',variables('resourceprefix'))]",
            "subnetRedisPrefix": "[concat( variables('octets')[0], '.', variables('octets')[1], '.', string(add(int(variables('octets')[2]),3)))]",
            "subnetRedisRange": "[concat( variables('octets')[0], '.', variables('octets')[1], '.', string(add(int(variables('octets')[2]),3)), '.0/24')]",
            "subnetSan": "[concat('san-subnet-',variables('resourceprefix'))]",
            "subnetSanPrefix": "[variables('subnetSanPrefix')]",
            "subnetSanRange": "[concat( variables('octets')[0], '.', variables('octets')[1], '.', string(add(int(variables('octets')[2]),2)), '.0/24')]",
            "subnetTika": "[concat('tika-subnet-',variables('resourceprefix'))]",
            "subnetTikaPrefix": "[concat( variables('octets')[0], '.', variables('octets')[1], '.', string(add(int(variables('octets')[2]),1)))]",
            "subnetTikaRange": "[concat( variables('octets')[0], '.', variables('octets')[1], '.', string(add(int(variables('octets')[2]),1)), '.0/24')]",
            "subnetWeb": "[concat('web-subnet-',variables('resourceprefix'))]",
            "subnetWebPrefix": "[concat( variables('octets')[0], '.', variables('octets')[1], '.', string(add(int(variables('octets')[2]),0)))]",
            "subnetWebRange": "[variables('subnetWebRange')]",
            "thumbprintSslCert": "[if(or(empty(parameters('keyVaultResourceId')), empty(parameters('sslCertThumbprint'))), 'None', parameters('sslCertThumbprint'))]",
            "thumbprintCaCert": "[if(or(empty(parameters('keyVaultResourceId')), empty(parameters('caCertThumbprint'))), 'None', parameters('caCertThumbprint'))]",
            "tikaNicName": "[concat('tika-vm-nic-',variables('resourceprefix'))]",
            "tikaScriptFilename": "install_tika.sh",
            "tikaService": "[parameters('tikaService')]",
            "tikaVmIP": "[if(equals(parameters('tikaService'), 'tika'), concat( variables('octets')[0], '.', variables('octets')[1], '.', string(add(int(variables('octets')[2]),1)), '.20'), 'none')]",
            "tikaVmName": "[concat('tika-vm-',variables('resourceprefix'))]",
            "tikaVmSku": "[parameters('tikaVmSku')]",
            "vNetAddressSpace": "[parameters('vNetAddressSpace')]",
            "vaultName": "[concat('vault-',variables('resourceprefix'))]",
            "vmssName": "[concat('vmss-',variables('resourceprefix'))]",
            "vmssdStorageAccounttName": "[concat('vmss',uniqueString(resourceGroup().id))]",
            "vnetDbDeploySwitch": "[and(parameters('vnetDbDeploySwitch'), equals(parameters('dbServerType'), 'mysqlflex'))]",
            "vnetGwDeploySwitch": "[parameters('vnetGwDeploySwitch')]",
            "vnetName": "[concat('vnet-',variables('resourceprefix'))]",
            "vpnType": "[parameters('vpnType')]",
            "webServerSetupScriptFilename": "setup_webserver.sh",
            "webServerType": "[parameters('webServerType')]",
            "isMigration": "[parameters('isMigration')]"
        },
        "certUrlArray": [
            {
                "certificateUrl": "[parameters('sslCertKeyVaultURL')]"
            },
            {
                "certificateUrl": "[parameters('caCertKeyVaultURL')]"
            }
        ],
        "ctlrVmSecretsArray": [
            {
                "sourceVault": {
                    "id": "[parameters('keyVaultResourceId')]"
                },
                "vaultCertificates": "[take(variables('certUrlArray'), if(empty(parameters('caCertKeyVaultURL')), 1, 2))]"
            }
        ],
        "octets": "[split(parameters('vNetAddressSpace'), '.')]",
        "resourceprefix": "[substring(uniqueString(resourceGroup().id, deployment().name), 3, 6)]",
        "subnetSanPrefix": "[concat( variables('octets')[0], '.', variables('octets')[1], '.', string(add(int(variables('octets')[2]),2)))]",
        "subnetWebRange": "[concat( variables('octets')[0], '.', variables('octets')[1], '.', string(add(int(variables('octets')[2]),0)), '.0/24')]"
    }
}


================================================
FILE: azuredeploy.parameters.json
================================================
{
    "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#",
    "contentVersion": "1.0.0.0",
        "parameters": {
                "sshPublicKey":                { "value": "GEN-SSH-PUB-KEY" },
                "fileServerDiskCount":         { "value": 2 },
                "fileServerDiskSize":          { "value": 32 }
        }
}


================================================
FILE: docs/Cleanup.md
================================================
# Cleanup All Resources

To cleanup a Moodle deployment simply delete the Resource Group that
contains it. The commands below will iterate over your workspace
directory and delete all deployments.

## Prerequisites

First we need to ensure our [environment variables](./Environment-Variables.md) are correctly configured.

## Remove each resource group

This command will delete all resources in *all* resource groups. Run
with caution.

Note, that this command will not fully delete the resource group if
you have Azure Backup enabled since the Recovery Services Vault will
not be deleted (it's got the backups of you data!).

``` bash
for filename in $MOODLE_AZURE_WORKSPACE/*; do az group delete --yes --name $(basename $filename) --no-wait; done
```


================================================
FILE: docs/Deploy.md
================================================
# Deploy Autoscaling Moodle Stack to Azure

After following the steps in this this document you with awill have a
new Moodle site with caching for speed and scaling frontends to handle
load. The filesystem behind it is mirrored for high availability and
optionally backed up through Azure. Filesystem permissions and options
have also been tuned to make Moodle more secure than a default
install.

## Prerequisites

To make things consitent across different sessions managing Moodle we
should [configure the environment](./Preparation.md).


## Create Resource Group

When you create the Moodle cluster you will create many resources. On
Azure it is a best practice to collect such resources together in a
Resource Group. The first thing we need to do, therefore, is create a
resource group:

```
az group create --name $MOODLE_RG_NAME --location $MOODLE_RG_LOCATION
```

Results:

```expected_similarity=0.4
{
  "id": "/subscriptions/325e7c34-99fb-4190-aa87-1df746c67705/resourceGroups/rgmoodlearm3",
  "location": "westus2",
  "managedBy": null,
  "name": "rgmoodlearm3",
  "properties": {
    "provisioningState": "Succeeded"
  },
  "tags": null
}
```

## Create Azure Deployment Parameters

Your deployment will be configured using an
`azuredeploy.parameters.json` file. It is possible to provide these
parameters interactively via the command line by simply omitting the
paramaters file in the command in the next section. However, it is
more reproducible if we use a paramaters file.

A good set of defaults are provided in the git repository. These
defaults create a scalable cluster that is suitable for low volume
testing. If you are building out a production service you should
review the section below on sizing considerations. For now we will
proceed with the defaults, but there is one value, the `sshPublicKey`
that **must** be provided. The following command will replace the
placeholder in the parameters template file with an SSH key used for
testing puporses (this is created as part of the envrionment setup in
the prerequisites):

``` bash
ssh_pub_key=`cat $MOODLE_SSH_KEY_FILENAME.pub`
echo $ssh_pub_key
sed "s|GEN-SSH-PUB-KEY|$ssh_pub_key|g" $MOODLE_AZURE_WORKSPACE/arm_template/azuredeploy.parameters.json > $MOODLE_AZURE_WORKSPACE/$MOODLE_RG_NAME/azuredeploy.parameters.json
```

If you'd like to configure the Moodle cluster (to be deployed)
with your own SSL certificate for your domain (siteURL) at the
deployment time, you can do so by using [Azure Key Vault](https://azure.microsoft.com/en-us/services/key-vault/)
and following the instructions in the [SSL cert documentation](SslCert.md).

For more information see the [parameters documentation](Parameters.md).

## Deploy cluster

Now that we have a resource group and a configuration file we can
create the cluster itself. This is done with a single command:

```
az deployment group create --name $MOODLE_DEPLOYMENT_NAME --resource-group $MOODLE_RG_NAME --template-file $MOODLE_AZURE_WORKSPACE/arm_template/azuredeploy.json --parameters $MOODLE_AZURE_WORKSPACE/$MOODLE_RG_NAME/azuredeploy.parameters.json
```

## Using the created stack

In testing, stacks typically took between 1 and 2 hours to finish,
depending on spec. Once complete you will receive a JSON output
containing information needed to manage your Moodle install (see
`outputs`). You can also retrieve this infromation from the portal or
the CLI.
                      
Once Moodle has been created, and (where necessary) you have
configured your custom `siteURL` DNS to point to the
`loadBalancerDNS`, you should be able to load the `siteURL` in a
browser and login with the username "admin" and the
`moodleAdminPassword`. Note that the values for each of these
parameters are avialble in the portal or the `outputs` section of the
JSON response from the previous deploy command. See [documentation on
how to retrieve configuration data](./Get-Install-Data.md) along
with full details of all the output parameters avialble to you.

Note that by default the deployment uses a self-signed certificate,
consequently you will recieve a warning when accessing the site. To
add a genuine certificate see the documentation on [managing your
cluster](./Manage.md).

## Sizing Considerations and Limitations

Depending on what you're doing with Moodle you will want to configure
your deployment appropriately.The defaults included produce a cluster
that is inexpensive but probably too low spec to use beyond simple
testing scenarios. This section includes an overview of how to size
the database and VM instances for your use case.

### Database Sizing

As of the time of this writing, Azure supports "Basic", "General Purpose" and "Memory Optimized"
tiers for MySQL/PostgreSQL database instances. In addition the mysqlPgresVcores defines
the number of vCores for each DB server instance, and the number of those you can use is limited by
database tier:

- Basic: 1, 2
- General Purpose: 2, 4, 8, 16, 32
- Memory Optimized: 2, 4, 8, 16

This value also limits the maximum number of connections, as defined
here: https://docs.microsoft.com/en-us/azure/mysql/concepts-limits

As the Moodle database will handle cron processes as well as the
website, any public facing website with more than 10 users will likely
require upgrading to 100. Once the site reaches 30+ users it will
require upgrading to General Purpose for more compute units. This depends
entirely on the individual site. As MySQL databases cannot change (or
be restored to a different tier) once deployed it is a good idea to
slightly overspec your database.

All MySQL/PostgreSQL database storage, regardless of tier, has a hard upper limit of 1
terabyte (1024 GB), starting from 5 GB minimum, increasing by 1 GB. You gain additional iops for each added GB, so if
you're expecting a heavy amount of traffic you will want to oversize
your storage. The current maximum iops with a 1TB disk is 3000.

### Controller instance sizing

The controller handles both syslog and cron duties. Depending on how
big your Moodle cron runs are this may not be sufficient. If cron jobs
are very delayed and cron processes are building up on the controller
then an upgrade in tier is needed.

### Frontend instances

In general the frontend instances will not be the source of any
bottlenecks unless they are severely undersized versus the rest of the
cluster. More powerful instances will be needed should fpm processes
spawn and exhaust memory during periods of heavy site load. This can
also be mitigated against by increasing the number of VMs but spawning
new VMs is slower (and potentially more expensive) than having that
capacity already available.

It is worth noting that the memory allowances on these instances allow
for more memory than they may be able to provide with lower instance
tiers. This is intentional as you can opt to run larger VMs with more
memory and not require manual configuration. FPM also allows for a
very large number of threads which prevents the system from failing
during many small jobs.


## Next Steps

  1. [Retrieve configuration details using CLI](./Get-Install-Data.md)
  1. [Manage the Moodle cluster](./Manage.md)


================================================
FILE: docs/Environment-Variables.md
================================================
# Environment Variables

In order to configure our deployment and tools we'll set up some
environment variables to ensure consistency. If you are running these
scripts through SimDem you can customize these values by copying and
editing `env.json` into `env.local.json`.

We'll need a unique name for our Resource Group in Azure, but when
running in an automated mode it is useful to have a (mostly) unique
name for your deployment and related resources. We'll use a timestamp.
If the environmnt variable `MOODLE_RG_NAME` is not set we will
create a new value using a timestamp:


``` shell
if [ -z "$MOODLE_RG_NAME" ]; then MOODLE_RG_NAME=moodle_$(date +%Y-%m-%d-%H); fi
```

Other configurable values for our Azure deployment include the
location and depoloyment name. We'll standardize these, but you can
use different values if you like.

``` shell
MOODLE_RG_LOCATION=southcentralus
MOODLE_DEPLOYMENT_NAME=MasterDeploy
```

We also need to provide an SSH key. Later we'll generate this if it
doesn't already exist but to enable us to reuse an existing key we'll
store it's filename in an Environment Variable.

``` shell
MOODLE_SSH_KEY_FILENAME=~/.ssh/moodle_id_rsa
```

We need a workspace for storing configuration files and other
per-deployment artifacts:

``` shell
MOODLE_AZURE_WORKSPACE=~/.moodle
```

## Create Workspace

Ensure the workspace for this particular deployment exists:

```
mkdir -p $MOODLE_AZURE_WORKSPACE/$MOODLE_RG_NAME
```

## Validation

After working through this file there should be a number of
environment variables defined that will be used to provide a common
setup for all our Moodle on Azure work.

The resource group name defines the name of the group into which all
resources will be, or are, deployed. 

```bash
echo "Resource Group for deployment: $MOODLE_RG_NAME"
```

Results:

```
Resource Group for deployment: southcentralus
```

The resource group location is:

```bash
echo "Deployment location: $MOODLE_RG_LOCATION"
```

Results:

```
Deployment location: southcentralus
```

When deploying a Moodle cluster the deployment will be given a name so
that it can be identified later should it be neceessary to debug.


```bash
echo "Deployment name: $MOODLE_DEPLOYMENT_NAME"
```

Results:

```
Deployment name: MasterDeploy
```

The SSH key to use can be found in a file, if necessary this will be
created as part of these scripts.

``` shell
echo "SSH key filename: $MOODLE_SSH_KEY_FILENAME"
```

Results:

```
SSH key filename: ~/.ssh/moodle_id_rsa
```

Configuration files will be written to / read from a customer directory:

``` shell
echo "Workspace directory: $MOODLE_AZURE_WORKSPACE"
```

Results:

```
Workspace directory: ~/.moodle
```

Ensure the workspace directory exists:


``` bash
if [ ! -f "$MOODLE_AZURE_WORKSPACE/$MOODLE_RG_NAME" ]; then echo "Worspace exists"; fi
```

Results:

```
Workspace exists
```


================================================
FILE: docs/Get-Install-Data.md
================================================
# Retrieve essential install details

Once a deployment has completed the ARM template will output some
values that you will need for managing your Moodle instalation. These
are available in the portal, but in this document we will retrieve
them using the AZ command line tools and through the AZ CLI tool. This
document describes the available parameters and how to retrieve them.

## Prerequisites

In order to configure our deployment and tools we'll set up some
[environment variables](./Environment-Variables.md) to ensure consistency.

## Output Paramater Overview

The available output parameters are:

  - **siteURL**: If you provided a `siteURL` parameter when deploying this
    will be set to the supplied value. Otherwise it will be the same as
    the loadBalancerDNS, see below.
  - **loadBalancerDNS**: This is the DNS name of your application load
    balancer. If you provided a `siteURL` parameter when deploying
    you'll need to add a DNS entry to its CNAMEs pointing to this address.
  - **moodleAdminPassword**: The generated password for the "admin" user
    in your Moodle install.
  - **controllerInstanceIP**: This is the IP address of the controller
    Virtual Machine. You will need to SSH into this to make changes to
    your Moodle code or view logs.
  - **databaseDNS**: This is the public DNS of your database instance. If
    you wish to set up local backups or access the DB directly, you'll
    need to use this.
  - **databaseAdminUsername**: The admin username for your database
    (this is not the same as your Moodle username).
  - **databaseAdminPassword**: The admin password for your
    database (this is not the same as your Moodle password).

## Retrieving Output Parameters Using the CLI

To get a complete list of outputs in json format use:

```bash
az deployment group show --resource-group $MOODLE_RG_NAME --name $MOODLE_DEPLOYMENT_NAME --out json --query *.outputs
```

Individual outputs can be retrieved by filtering, for example, to get
just the value of the `siteURL` use:

``` bash
az deployment group show --resource-group $MOODLE_RG_NAME --name $MOODLE_DEPLOYMENT_NAME --out json --query *.outputs.siteURL.value
```

However, since we are reqeusting JSON output (the default) the value
is enclosed in quotes. In order to remove these we can output as a tab
separated list (TSV):

``` bash
az deployment group show --resource-group $MOODLE_RG_NAME --name $MOODLE_DEPLOYMENT_NAME --out tsv --query *.outputs.siteURL
```

Now we can assign individual values to environment variables, for example:

``` bash
MOODLE_SITE_URL="$(az deployment group show --resource-group $MOODLE_RG_NAME --name $MOODLE_DEPLOYMENT_NAME --out tsv --query *.outputs.siteURL.value)"
```

### Retrieving Moodle Site URL

The Site URL is the value used to configure Moodle's base URL. The
site URL can be provided as an input to the template via the parameter
`siteURL`, in which case you will not need to retrieve this from the
outputs. However, if you do not define this, or if you leave it as the
default "www.example.org" you will need to retrieve this value from
Azure using the following command:

```bash
MOODLE_SITE_URL="$(az deployment group show --resource-group $MOODLE_RG_NAME --name $MOODLE_DEPLOYMENT_NAME --out tsv --query *.outputs.siteURL.value)"
```

#### Retrieving Moodle Site Load Balancer URL

The load balancer DNS is the publicly registered DNS name for your
Moodle DNS. If this is different from the site URL it is important to
ensure that you configure your DNS entry for site URL to point at the
load balancer.

```bash
MOODLE_LOAD_BALANCER_DNS="$(az deployment group show --resource-group $MOODLE_RG_NAME --name $MOODLE_DEPLOYMENT_NAME --out tsv --query *.outputs.loadBalancerDNS.value)"
```

### Retrieving Moodle Administrator Password

Moodle admin password (username is "admin"):

```bash
MOODLE_ADMIN_PASSWORD="$(az deployment group show --resource-group $MOODLE_RG_NAME --name $MOODLE_DEPLOYMENT_NAME --out tsv --query *.outputs.moodleAdminPassword.value)"
```

### Retriving Controller Virtual Machine Details

The controller VM runs management tasks for the cluster, such as cron jobs and syslog.

```bash
MOODLE_CONTROLLER_INSTANCE_IP="$(az deployment group show --resource-group $MOODLE_RG_NAME --name $MOODLE_DEPLOYMENT_NAME --out tsv --query *.outputs.controllerInstanceIP.value)"
```

There is no username and password for this VM since a username and SSH
key are provided as input parameters to the template.

### Retreiving Database Information

#### Database URL

``` bash
MOODLE_DATABASE_DNS="$(az deployment group show --resource-group $MOODLE_RG_NAME --name $MOODLE_DEPLOYMENT_NAME --out tsv --query *.outputs.databaseDNS.value)"
```
#### Database admin username

``` bash
MOODLE_DATABASE_ADMIN_USERNAME="$(az deployment group show --resource-group $MOODLE_RG_NAME --name $MOODLE_DEPLOYMENT_NAME --out tsv --query *.outputs.databaseAdminUsername.value)"
```

#### Database admin password

``` bash
MOODLE_DATABASE_ADMIN_PASSWORD="$(az deployment group show --resource-group $MOODLE_RG_NAME --name $MOODLE_DEPLOYMENT_NAME --out tsv --query *.outputs.databaseAdminPassword.value)"
```

### Retrieving Moodle Application VNET Information

First frontend VM IP:

``` bash
MOODLE_FIRST_FRONTEND_VM_IP="$(az deployment group show --resource-group $MOODLE_RG_NAME --name $MOODLE_DEPLOYMENT_NAME --out tsv --query *.outputs.firstFrontendVmIP.value)"
```

# Validation

After having run each of the commands in this document you should have
each of the output parameters available in environment variable:

``` bash
echo $MOODLE_SITE_URL
echo $MOODLE_LOAD_BALANCER_DNS
echo $MOODLE_ADMIN_PASSWORD
echo $MOODLE_CONTROLLER_INSTANCE_IP
echo $MOODLE_DATABASE_DNS
echo $MOODLE_DATABASE_ADMIN_USERNAME
echo $MOODLE_DATABASE_ADMIN_PASSWORD
echo $MOODLE_FIRST_FRONTEND_VM_IP
```

## Next Steps

  1. [Manage the Moodle cluster](./Manage.md)


================================================
FILE: docs/Manage.md
================================================
# Managing a Scalable Moodle Cluster in Azure

This document provides an overview of how to perform various
management tasks on a scalable Moodle cluster on Azure.

## Prerequisites

In order to configure our deployment and tools we'll set up some
[environment variables](./Environment-Variables.md) to ensure consistency.

In order to manage a cluster it is clearly necessary to first [deploy
a scalable Moodle cluster on Azure](./Deploy.md).

For convenience and readability this document also assumes that essential [deployment details for your cluster have been assigned to environment variables](./Get-Install-Data.md).

## Updating Moodle code/settings

Your controller Virtual Machine has Moodle code and data stored in
`/moodle`. The site code is stored in `/moodle/html/moodle/`. This
data is replicated across dual gluster nodes to provide high
availability. This directory is also mounted to your autoscaled
frontends so all changes to files on the controller VM are immediately
available to all frontend machines (when the `htmlLocalCopySwitch` in `azuredeploy.json`
is false--otherwise, see below). Note that any updates on Moodle code/settings
(e.g., additional plugin installations, Moodle version upgrade) have to be done
on the controller VM using shell commands, not through a web browser, because the
HTML directory's permission is read-only for the web frontend VMs (thus any web-based
Moodle code updates will fail).

Depending on how large your Gluster disks are sized, it may be helpful
to keep multiple older versions (/moodle/html1, /moodle/html2, etc) to
roll back if needed.

To connect to your Controller VM use SSH with a username of
'azureuser' and the SSH provided in the `sshPublicKey` input
parameter. For example, to retrieve a listing of files and directories
in the `/moodle` directory use:

```
ssh -o StrictHostKeyChecking=no azureadmin@$MOODLE_CONTROLLER_INSTANCE_IP ls -l /moodle
```

Results:

```
Warning: Permanently added '52.228.45.38' (ECDSA) to the list of known hosts.
total 12
drwxr-xr-x  2 www-data www-data 4096 Jan 17 00:59 certs
-rw-r--r--  1 root     root        0 Jan 17 02:22 db-backup.sql
drwxr-xr-x  3 www-data www-data 4096 Jan 17 00:54 html
drwxrwx--- 10 www-data www-data 4096 Jan 17 06:55 moodledata
```

**IMPORTANT NOTE**

It is important to realize that the `-o StrictHostKeyChecking=no`
option in the above SSH command presents a security risk. It is
included here to facilitate automated validation of these commands. It
is not recommended to use this option in production environments,
instead run the command manually and validate the host key.
Subsequent executions of an SSH command will not require this
validation step. For more information there is an excellent
[superuser.com
Q&A](https://superuser.com/questions/421074/ssh-the-authenticity-of-host-host-cant-be-established/421084#421084).

### If you set `htmlLocalCopySwitch` to true (this is the default now)

Originally the `/moodle/html` directory was shared across all autoscaled
web VMs through the specified file server (Gluster or NFS), and this is
not good for web response time. Therefore, we introduced the
`htmlLocalCopySwitch` that'll copy the `/moodle/html` directory to
`/var/www/html` in each autoscaled web VM and reconfigures the web
server (apache/nginx)'s server root directory accordingly, when it's set
to true. This now requires directory sync between `/moodle/html` and
`/var/www/html`, and currently it's addressed by simple polling
(minutely). Therefore, if you are going to update your Moodle
code/settings with the switch set to true, please follow the
following steps:

* Put your Moodle site to maintenance mode.
  * This will need to be done on the contoller VM with some shell command.
  * It should be followed by running the following command to propagate the change to all autoscaled web VMs:
    ```bash
    $ sudo /usr/local/bin/update_last_modified_time.moodle_on_azure.sh
    ```
  * Once this command is executed, each autoscaled web VM will pick up (sync) the changes within 1 minute, so wait for one minute.
* Then you can start updating your Moodle code/settings, like installing/updating plugins or upgrading Moodle version or changing Moodle configurations. Again, note that this should be all done on the controller VM using some shell commands.
* When you are done updating your Moodle code/settings, run the same command as above to let each autoscaled web VM pick up (sync) the changes (wait for another minute here, for the same reason).

Please do let us know on this Github repo's Issues if you encounter any problems with this process.

## Getting an SQL dump

By default a daily sql dump of your database is taken at 02:22 and
saved to `/moodle/db-backup.sql`(.gz). This file can be retrieved
using SCP or similar. For example:

``` bash
scp azureadmin@$MOODLE_CONTROLLER_INSTANCE_IP:/moodle/db-backup.sql /tmp/moodle-db-backup.sql
```

To obtain a more recent SQL dump you run the commands appropriate for
your chosen database on the Controller VM. The following sections will
help with this task.

#### Postgres

Postgress provides a `pg_dump` command that can be used to take a
snapshot of the database via SSH. For example, use the following
command:

``` bash
ssh azureadmin@$MOODLE_CONTROLLER_INSTANCE_IP 'pg_dump -Fc -h $MOODLE_DATABASE_DNS -U $MOODLE_DATABASE_ADMIN_USERNAME moodle > /moodle/db-snapshot.sql'
```

See the Postgres documentation for full details of the [`pg_dump`](https://www.postgresql.org/docs/9.5/static/backup-dump.html) command.

#### MySQL

MySQL provides a `mysql_dump` command that can be used to take a
snapshot of the database via SSH. For example, use the following
command:

``` bash
ssh azureadmin@$MOODLE_CONTROLLER_INSTANCE_IP 'mysqldump -h $mysqlIP -u ${azuremoodledbuser} -p'${moodledbpass}' --databases ${moodledbname} | gzip > /moodle/db-backup.sql.gz'
```

## Backup and Recovery

If you have set the `azureBackupSwitch` in the input parameters to `1`
then Azure will provide VM backups of your Gluster node. This is
recommended as it contains both your Moodle code and your sitedata.
Restoring a backed up VM is outside the scope of this doc, but Azure's
documentation on Recovery Services can be found here:
https://docs.microsoft.com/en-us/azure/backup/backup-azure-vms-first-look-arm

## Resizing your Database

Note: This process involves site downtime and should therefore only be
carried out during a planned maintenance window.

At the time of writing Azure does not support resizing MySQL or
Postgres databases. You can, however, create a new database instance,
with a different size, and change your config to point to that. To get
a different size database you'll need to:

  1. [Place your Moodle site into maintenance
     mode](https://docs.moodle.org/34/en/Maintenance_mode). You can do
     this either via the web interface or the command line on the
     controller VM.
  2. Perform an SQL dump of your database. See above for more details.
  3. Create a new Azure database of the size you want inside your
     existing resource group.
  4. Using the details in your /moodle/html/moodle/config.php create a
     new user and database matching the details in config.php. Make
     sure to grant all rights on the db to the user.
  5. On the controller instance, change the db setting in
     /moodle/html/moodle/config.php to point to the new database.
  6. Take Moodle site out of maintenance mode.
  7. Once confirmed working, delete the previous database instance.

How long this takes depends entirely on the size of your database and
the speed of your VM tier. It will always be a large enough window to
make a noticeable outage.

## Changing the SSL cert

The self-signed cert generated by the template is suitable for very
basic testing, but a public website will want a real cert. After
purchasing a trusted certificate, it can be copied to the following
files to be ready immediately:

  - /moodle/certs/nginx.key: Your certificate's private key
  - /moodle/certs/nginx.crt: Your combined signed certificate and trust chain certificate(s).

## Managing Azure DDoS protection

By default, every plublic IP is protected by Azure DDoS protection Basic SKU. 
You can find more information about Azure DDoS protection Basic SKU [here](https://docs.microsoft.com/en-us/azure/virtual-network/ddos-protection-overview).

If you want more protection, you can activate Azure DDoS protection Standard SKU by setting 
the ddosSwith to true. You can find how to work with Azure DDoS 
protection plan [here](https://docs.microsoft.com/en-us/azure/virtual-network/manage-ddos-protection#work-with-ddos-protection-plans).

If you want to disable the Azure DDoS protection, you can follow the instruction 
[here](https://docs.microsoft.com/en-us/azure/virtual-network/manage-ddos-protection#disable-ddos-for-a-virtual-network). 

Be careful, disabling the Azure DDoS protection on your vnet will not stop the fee.
You have to delete the Azure DDoS protection plan if you want to stop the fee.

If you have deployed your cluster without Azure DDoS protection plan, you still can activate the 
Azure DDoS protection plan thanks to the instruction [here](https://docs.microsoft.com/en-us/azure/virtual-network/manage-ddos-protection#enable-ddos-for-an-existing-virtual-network).

## Next Steps

  1. [Retrieve configuration details using CLI](./Get-Install-Data.md)


================================================
FILE: docs/Parameters.md
================================================
# Moodle on Azure Parameters

Our goal with these templates is to make it as easy as possible to
deploy a Moodle on Azure cluster that can be customized to your
specific needs. To that end we provide a great manay configuration
options. This document attempts to document all these parameters,
however, like all documentation it can sometimes fall behind. For a
canonical reference you should review the `azuredeploy.json` file.

## Extracting documentation from azuredeploy.json

To make it a litte easier to read `azuredeploy.json` you might want to
run the following commands which will extract the necessary
information and display it in a more readable form.

```bash
sudo apt install jq
```

``` bash
jq -r '.parameters | to_entries[] | "### " + .key + "\n\n" + .value.metadata.description + "\n\nType: " + .value.type + "\n\nPossible Values: " + (.value.allowedValues | @text) + "\n\nDefault: " + (.value.defaultValue | @text) + "\n\n"' azuredeploy.json
```

## Available Parameters

### _artifactsLocation

The base URI where artifacts required by this template are located. When the template is deployed using the accompanying scripts, a private location in the subscription will be used and this value will be automatically generated.

Type: string

Possible Values: null

Default: https://raw.githubusercontent.com/Azure/Moodle/master/


### _artifactsLocationSasToken

The sasToken required to access _artifactsLocation.  When the template is deployed using the accompanying scripts, a sasToken will be automatically generated.

Type: securestring

Possible Values: null

Default: 


### applyScriptsSwitch

Switch to process or bypass all scripts/extensions

Type: bool

Possible Values: null

Default: true


### azureBackupSwitch

Switch to configure AzureBackup and enlist VM's

Type: bool

Possible Values: null

Default: false


### redisDeploySwitch

Switch to deploy a redis cache or not. Note that certain versions of Moodle (e.g., 3.1) don't work well with Redis, so use this only for known well-working Moodle versions (e.g., 3.4).

Type: bool

Possible Values: null

Default: false


### vnetGwDeploySwitch

Switch to deploy a virtual network gateway or not

Type: bool

Possible Values: null

Default: false


### installObjectFsSwitch

Switch to install Moodle Object FS plugins (with Azure Blob storage)

Type: bool

Possible Values: null

Default: false


### installO365pluginsSwitch

Switch to install Moodle Office 365 plugins. As of May 22, 2018, O365 plugins for Moodle 3.5 haven't been released, so to set this true, you must set the moodleVersion to 3.4 or below.

Type: bool

Possible Values: null

Default: false


### installGdprPluginsSwitch

(Should be used only for Moodle 3.4 & 3.3) Switch to install Moodle GDPR plugins. Note these require Moodle versions 3.4.2+ or 3.3.5+ and these are included by default in Moodle 3.5. So if you choose MOODLE_35_STABLE as your moodleVersion, do not set this to true.

Type: bool

Possible Values: null

Default: false


### htmlLocalCopySwitch

Switch to create a local copy of /moodle/html or not

Type: bool

Possible Values: null

Default: true


### ddosSwitch

Switch to create a DDoS protection plan

Type: bool

Possible Values: null

Default: false


### enableAccelNwForCtlrVmSwitch

Switch to enable Azure Accelerated Networking on the controller VM. Default to false because currently the default controller VM SKU (D1) doesn't support AN. Change this to true if you set the controller VM SKU to eligibible ones (e.g., D2) for better performance.

Type: bool

Possible Values: null

Default: false


### enableAccelNwForOtherVmsSwitch

Switch to enable Azure Accelerated Networking on all other VMs. Default to true because currently the default controller VM SKU for all other VMS (D2) does support AN. Change this to false if you set the SKU of any other VMs to an ineligibible one (e.g., D1) to avoid deployment failure.

Type: bool

Possible Values: null

Default: true


### httpsTermination

Indicates where https termination occurs. 'VMSS' is for https termination at the VMSS instance VMs (using nginx https proxy). 'AppGw' is for https termination with an Azure Application Gateway. When selecting this, you need to specify all appGw* parameters. 'None' is for testing only with no https. 'None' may not be used with a separately configured https termination layer. If you want to use the 'None' option with your separately configured https termination layer, you'll need to update your Moodle config.php manually for $cfg->wwwroot and $cfg->sslproxy.

Type: string

Possible Values: ["VMSS","AppGw","None"]

Default: VMSS


### siteURL

URL for Moodle site

Type: string

Possible Values: null

Default: www.example.org


### moodleVersion

The Moodle version you want to install.

Type: string

Possible Values: ["MOODLE_35_STABLE","MOODLE_34_STABLE","v3.4.3","v3.4.2","v3.4.1","MOODLE_33_STABLE","MOODLE_32_STABLE","MOODLE_31_STABLE","MOODLE_30_STABLE","MOODLE_29_STABLE"]

Default: MOODLE_35_STABLE


### sshPublicKey

ssh public key

Type: string

Possible Values: null

Default: null


### sshUsername

ssh user name

Type: string

Possible Values: null

Default: azureadmin


### controllerVmSku

VM size for the controller VM

Type: string

Possible Values: null

Default: Standard_DS1_v2


### webServerType

Web server type

Type: string

Possible Values: ["apache","nginx"]

Default: apache


### autoscaleVmSku

VM size for autoscaled web VMs

Type: string

Possible Values: null

Default: Standard_DS2_v2


### autoscaleVmCountMax

Maximum number of autoscaled web VMs

Type: int

Possible Values: null

Default: 10


### autoscaleVmCountMin

Minimum (also initial) number of autoscaled web VMs

Type: int

Possible Values: null

Default: 1


### osDiskStorageType

Azure storage type for all VMs' OS disks. With htmlLocalCopySwith true, Premium_LRS (SSD) is strongly recommended, as PHP files will be served from OS disks.

Type: string

Possible Values: ["Premium_LRS","Standard_LRS"]

Default: Premium_LRS


### dbServerType

Database type

Type: string

Possible Values: ["postgres","mysql","mssql"]

Default: mysql


### dbLogin

Database admin username

Type: string

Possible Values: null

Default: dbadmin


### mysqlPgresVcores

MySql/Postgresql vCores. For Basic tier, only 1 & 2 are allowed. For GeneralPurpose tier, 2, 4, 8, 16, 32 are allowed. For MemoryOptimized, 2, 4, 8, 16 are allowed.

Type: int

Possible Values: [1,2,4,8,16,32]

Default: 2


### mysqlPgresStgSizeGB

MySql/Postgresql storage size in GB. Minimum 5GB, increase by 1GB, up to 1TB (1024 GB)

Type: int

Possible Values: null

Default: 125


### mysqlPgresSkuTier

MySql/Postgresql sku tier

Type: string

Possible Values: ["Basic","GeneralPurpose","MemoryOptimized"]

Default: GeneralPurpose


### mysqlPgresSkuHwFamily

MySql/Postgresql sku hardware family. Central US is Gen4 only, so make sure to change this parameter to Gen4 if your deployment is on Central US.

Type: string

Possible Values: ["Gen4","Gen5"]

Default: Gen5


### mysqlVersion

Mysql version

Type: string

Possible Values: ["5.6","5.7"]

Default: 5.7


### postgresVersion

Postgresql version

Type: string

Possible Values: ["9.5","9.6"]

Default: 9.6


### sslEnforcement

MySql/Postgresql SSL connection

Type: string

Possible Values: ["Disabled","Enabled"]

Default: Disabled


### mssqlDbServiceObjectiveName

MS SQL database service object names

Type: string

Possible Values: ["S1","S2","S3","S4","S5","S6","S7","S9"]

Default: S1


### mssqlDbSize

MS SQL database size

Type: string

Possible Values: ["100MB","250MB","500MB","1GB","2GB","5GB","10GB","20GB","30GB","40GB","50GB","100GB","250GB","300GB","400GB","500GB","750GB","1024GB"]

Default: 250GB


### mssqlDbEdition

MS SQL DB edition

Type: string

Possible Values: ["Basic","Standard"]

Default: Standard


### mssqlVersion

Mssql version

Type: string

Possible Values: ["12.0"]

Default: 12.0


### fileServerType

File server type: GlusterFS, NFS, and NFS-HA (2-VM highly available NFS cluster)

Type: string

Possible Values: ["gluster","nfs","nfs-ha","nfs-byo"]

Default: nfs


### nfsByoIpExportPath

IP address and export path of the BYO-NFS share when fileServerType == nfs-byo. E.g., 172.16.1.8:/msazure

Type: string

Possible Values: null

Default: 


### fileServerDiskSize

Size per disk for gluster nodes or nfs server

Type: int

Possible Values: null

Default: 127


### fileServerDiskCount

Number of disks in raid0 per gluster node or nfs server

Type: int

Possible Values: null

Default: 4


### fileServerVmSku

VM size for the gluster or NFS-HA nodes

Type: string

Possible Values: null

Default: Standard_DS2_v2


### keyVaultResourceId

(VMSS https termination only) Azure Resource Manager resource ID of the Key Vault in case you stored your SSL cert in an Azure Key Vault (Note that this Key Vault must have been pre-created on the same Azure region where this template is being deployed). Leave this blank if you didn't. Resource ID example: /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/xxx/providers/Microsoft.KeyVault/vaults/yyy. This value can be obtained from keyvault.sh output if you used the script to store your SSL cert in your Key Vault.

Type: string

Possible Values: null

Default: 


### sslCertKeyVaultURL

(VMSS https termination only) Azure Key Vault URL for your stored SSL cert. This value can be obtained from keyvault.sh output if you used the script to store your SSL cert in your Key Vault. This parameter is ignored if the keyVaultResourceId parameter is blank.

Type: string

Possible Values: null

Default: 


### sslCertThumbprint

(VMSS https termination only) Thumbprint of your stored SSL cert. This value can be obtained from keyvault.sh output if you used the script to store your SSL cert in your Key Vault. This parameter is ignored if the keyVaultResourceId parameter is blank.

Type: string

Possible Values: null

Default: 


### caCertKeyVaultURL

(VMSS https termination only) Azure Key Vault URL for your stored CA (Certificate Authority) cert. This value can be obtained from keyvault.sh output if you used the script to store your CA cert in your Key Vault. This parameter is ignored if the keyVaultResourceId parameter is blank.

Type: string

Possible Values: null

Default: 


### caCertThumbprint

(VMSS https termination only) Thumbprint of your stored CA cert. This value can be obtained from keyvault.sh output if you used the script to store your CA cert in your Key Vault. This parameter is ignored if the keyVaultResourceId parameter is blank.

Type: string

Possible Values: null

Default: 


### appGwSslCertKeyVaultResourceId

(App Gateway https termination only) Azure Key Vault URL for your stored SSL cert, again for App Gateway https termination case only. (Note that this Key Vault must have been pre-created on the same Azure region where this template is being deployed). Leave this blank if you didn't. Resource ID example: /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/xxx/providers/Microsoft.KeyVault/vaults/yyy.

Type: string

Possible Values: null

Default: 


### appGwSslCertKeyVaultSecretName

(App Gateway https termination only) Name of the Azure Key Vault secret that's stored in the previously specified Key Vault as a PFX certificate (with no password) for your site's SSL cert. This secret must be pre-populated in the specified Key Vault with the matching name.

Type: string

Possible Values: null

Default: 


### appGwSkuName

(App Gateway https termination only) Name of the Applicate Gateway SKU

Type: string

Possible Values: ["Standard_Small","Standard_Medium","Standard_Large","WAF_Medium","WAF_Large"]

Default: Standard_Medium


### appGwSkuTier

(App Gateway https termination only) Tier of the Applicate Gateway

Type: string

Possible Values: ["Standard","WAF"]

Default: Standard


### appGwSkuCapacity

(App Gateway https termination only) Capacity instance count) of the Applicate Gateway

Type: int

Possible Values: null

Default: 2


### storageAccountType

Storage Account type. This storage account is only for the Moodle ObjectFS plugin and/or the (currently disabled) Azure Files file share option

Type: string

Possible Values: ["Standard_LRS","Standard_GRS","Standard_ZRS"]

Default: Standard_LRS


### searchType

options of moodle global search

Type: string

Possible Values: ["none","azure","elastic"]

Default: none


### tikaService

options of enabling tika service for file searching in moodle

Type: string

Possible Values: ["none","tika"]

Default: none


### azureSearchSku

the search service level you want to create.

Type: string

Possible Values: ["free","basic","standard","standard2","standard3"]

Default: basic


### azureSearchReplicaCount

Replicas distribute search workloads across the service. You need 2 or more to support high availability (applies to Basic and Standard only).

Type: int

Possible Values: null

Default: 3


### azureSearchPartitionCount

Partitions allow for scaling of document count as well as faster indexing by sharding your index over multiple Azure Search units.

Type: int

Possible Values: [1,2,3,4,6,12]

Default: 1


### azureSearchHostingMode

Applicable only for azureSearchSku set to standard3. You can set this property to enable a single, high density partition that allows up to 1000 indexes, which is much higher than the maximum indexes allowed for any other azureSearchSku.

Type: string

Possible Values: ["default","highDensity"]

Default: default


### elasticVmSku

VM size for the elastic search nodes

Type: string

Possible Values: null

Default: Standard_DS2_v2


### tikaVmSku

VM size for the tika search nodes

Type: string

Possible Values: null

Default: Standard_DS2_v2


### customVnetId

Azure Resource ID of the Azure virtual network where you want to deploy your Moodle resources. A vnet resource ID is of the following format: /subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxxxxx/resourceGroups/gggg/providers/Microsoft.Network/virtualNetworks/vvvv. Note that this virtual network must be on the same Azure location as this template deployment location. If this parameter is blank, a new Azure virtual network will be created and used. In that case, the address space of the newly created virtual network will be */16 of the following vNetAddressSpace parameter value below.

Type: string

Possible Values: null

Default: 


### vNetAddressSpace

Address range for the Moodle virtual network and various subnets - presumed /16 for a newly created vnet in case customVnetId is blank. Further subneting (a number of */24 subnets starting from the xxx.yyy.zzz.0/24 will be created on a newly created vnet or your BYO-vnet (specified in customVnetId parameter).

Type: string

Possible Values: null

Default: 172.31.0.0


### gatewayType

Virtual network gateway type

Type: string

Possible Values: ["Vpn","ER"]

Default: Vpn


### vpnType

Virtual network gateway vpn type

Type: string

Possible Values: ["RouteBased","PolicyBased"]

Default: RouteBased


### loadBalancerSku

Loadbalancer SKU

Type: string

Possible Values: ["Basic","Standard"]

Default: Basic


### location

Azure Location for all resources.

Type: string

Possible Values: null

Default: [resourceGroup().location]




================================================
FILE: docs/Preparation.md
================================================
# Environment Preparation

This document describes how to ensure your environment is configured
for working with Moodle on Azure.

## Prerequisites

In order to configure our deployment and tools we'll set up some
[environment variables](./Environment-Variables.md) to ensure consistency.

## Required software

We'll use a number of tools when working with Moodle on Azure. Let's
ensure they are all installed:

``` shell
sudo apt-get update
sudo apt-get install wget -y
sudo apt-get openssh-client -y
```

The [Azure CLI](https://docs.microsoft.com/en-us/cli/azure/install-azure-cli-apt?view=azure-cli-latest) is also important:

```bash
AZ_REPO=$(lsb_release -cs)
echo "deb [arch=amd64] https://packages.microsoft.com/repos/azure-cli/ $AZ_REPO main" | sudo tee /etc/apt/sources.list.d/azure-cli.list
sudo apt-key adv --keyserver packages.microsoft.com --recv-keys 52E16F86FEE04B979B07E28DB02C46DF417A0893
sudo apt-get install apt-transport-https
sudo apt-get update && sudo apt-get install azure-cli
```

## Ensure we have a valid SSH key pair

We use SSH for secure communication with our hosts. The following line
will check there is a valid SSH key available and, if not, create one.

```
if [ ! -f "$MOODLE_SSH_KEY_FILENAME" ]; then ssh-keygen -t rsa -N "" -f $MOODLE_SSH_KEY_FILENAME; fi
```
## Checkout the Moodle ARM Template

The Moodle Azure Resource Manager template is hosted on GitHub. We'll
checkout the template into our workspace.

```
git clone git@github.com:Azure/Moodle.git $MOODLE_AZURE_WORKSPACE/arm_template
```

# Validation

After completing these steps we should have, amonst other things, a
complete checkout of the Moodle templates from GitHub:

``` bash
ls $MOODLE_AZURE_WORKSPACE/arm_template
```

Results:

``` expected_similarity=0.4
azuredeploy.json  azuredeploy.parameters.json  CONTRIBUTE.md  docs  env.json  etc  images  LICENSE  LICENSE-DOCS  metadata.json  nested
README.md
```

We should also have a number of applications installed, such as the Azure CLI:

``` bash
if hash az 2>/dev/null; then echo "Azure CLI Installed"; else echo "Missing dependency: Azure CLI"; fi
```

```
AzureCLI Installed
```


================================================
FILE: docs/SslCert.md
================================================
# SSL Certificate Management

A valid SSL (TLS) certificate should be used with your domain name for the Moodle
site to be deployed using the templates. By default, the templates will configure
the HTTPS server with a self-signed SSL server certificate/private key, which can
be manually changed with your own valid SSL server certificate/private key after
the deployment.

If you'd like to configure the Moodle cluster (to be deployed) with your own domain
and your valid SSL server certificate/private key, then you can do so by utilizing
[Azure Key Vault](https://azure.microsoft.com/en-us/services/key-vault/) and
configuring the related template parameters as described below. This support is
based on [another similar work](https://github.com/Azure/azure-quickstart-templates/tree/master/201-vmss-ubuntu-web-ssl)
and adapted to our situation.

## Initial deployment

To configure the Moodle cluster (to be deployed) with your purchased SSL certificate,
currently the related files should be stored in an Azure Key Vault as secrets, so that
Azure Resource Manager can reference when it deploys VMs as specified in templates.

You can create your own Azure Key Vault and store your purchased SSL certificate (called
'import' in Azure Key Vault terminology) by following related documentation like
[this](https://docs.microsoft.com/en-us/azure/key-vault/key-vault-manage-with-cli2).
However, the related files must be stored in a specific format, so we created a
shell script (`keyvault.sh`) that will perform all necessary steps for this purpose.
To use this script, you'll first need to upload your SSL certificate/private key files
(.pem) to your deployment environment you set up by following the [preparation document](Preparation.md)
(a Linux command line). The .pem files should be as follows:

- `cert.pem`: The SSL certificate only in PEM format
- `key.pem`: The private key for the SSL certificate only in PEM format
- `chain.pem`: This is optional in case your server certificate is signed by an intermediate CA (Certificate Authority) certificate, instead of a root CA certificate. Currently only one intermediate CA certificate is supported by the script.

Once you updloaded the files to your deployment environment, you can run the following command
to create an Azure Key Vault on your subscription and store your SSL certificate, private key, and optionally
the intermediate CA certificate:

``` bash
$ bash $MOODLE_AZURE_WORKSPACE/arm_template/etc/keyvault.sh <key_vault_name> <resource_group_name> <azure_region> <secret_name> cert.pem key.pem chain.pem
```

Make sure to set `<azure_region>` the same as the Azure region you'll be using to deploy the Moodle template.
Assign desired names for `<key_vault_name>`, `<resource_group_name>` (you can use an existing resource group) and `<secret_name>`.
`<secret_name>` is not very important in our deployment. Then you'll get outputs as follows:

```
...
Specified SSL cert/key .pem files are now stored in your Azure Key Vault and ready to be used by the template.
Use the following values for the related template parameters:

- keyVaultResourceId: /subscriptions/206c66fc-a48c-480d-ad06-0d429e82c586/resourceGroups/keyvault/providers/Microsoft.KeyVault/vaults/mdl-kv
- sslCertKeyVaultURL: https://mdl-kv.vault.azure.net/secrets/mymoodlesitecert/4c88452fe72b4d469253af48348f4944
- sslCertThumbprint:  56478E4F9555662476E2763D909F50B3DD26FF84
- caCertKeyVaultURL:  https://mdl-kv.vault.azure.net/secrets/camymoodlesitecert/684efab1f2124e71a2c809457d10808b
- caCertThumbprint:   E6A3B45B062D509B3382282D196EFE97D5956CCB
Done
```

This example outputs assumes `"keyvault"` is used for `<resource_group_name>`, `"mdl-kv"` for `<key_vault_name>`,
and `"mymoodlesitecert"` for `<secret_name>`. Note that `caCertKeyVaultURL` and `caCertThumbprint` will be empty
if you didn't specify `chain.pem`. Then you can copy these outputs to the template's corresponding parameters,
and Azure Resource Manager will install the certificate and the private key on the deployed VMs and the deployed
HTTPS server will use this certificate and private key.

## Certificate rotation

Another important benefit of using Azure Key Vault is to handle certificate expiration/rotation automatically.
Unfortunately, the current implementation doesn't support the auto-rotation. So when it becomes near your SSL
certificate's expiry, you'll need to manually update the deployed certificate and private key files
(it's in `/moodle/certs/nginx.{crt,key}` on the controller VM) and restart all the web frontend VM instances.
We'll improve our implementation to support auto-rotation in the future.

================================================
FILE: docs/Test.md
================================================
# Test a Moodle Instance

## Prerequisites

It is obviously necessary to have a [Moodle cluster up and running](./Deploy.md).

## Next Steps

  * [Delete all Resources](./Delete.md)


================================================
FILE: docs/env.json
================================================
{
    "MOODLE_RG_NAME": "rgmoodlearm13tagging",
    "MOODLE_RG_LOCATION": "canadacentral",
    "MOODLE_DEPLOYMENT_NAME": "MainDeployment"
}


================================================
FILE: env.json
================================================
{
    "MOODLE_RG_NAME": "rgmoodlearm12",
    "MOODLE_RG_LOCATION": "canadacentral",
    "MOODLE_DEPLOYMENT_NAME": "MainDeployment"
}


================================================
FILE: etc/changeBranchInURL.sh
================================================
#!/bin/bash

from=$1
to=$2

sed -i s/%2F${from}%2F/%2F${to}%2F/g README.md
sed -i s#/${from}/#/${to}/#g azuredeploy.json


================================================
FILE: etc/checkBaseUrls.sh
================================================
# Ensure that the Base URL for templates, scripts and deploy to Azure buttons
# is correctly set for the current git branch.

# Correct values for locations
CURRENT_BRANCH=$(git branch | sed -n -e 's/^\* \(.*\)/\1/p')
echo "Current git branch is '$CURRENT_BRANCH'"

BASE_TEMPLATE_URL=https://raw.githubusercontent.com/Azure/Moodle/$CURRENT_BRANCH/nested/
echo "Base template URL: $BASE_TEMPLATE_URL"

SCRIPT_LOCATION=https://raw.githubusercontent.com/Azure/Moodle/$CURRENT_BRANCH/scripts/
echo "Script location: $SCRIPT_LOCATION"

DEPLOY_TO_AZURE_URL=https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2FAzure%2FMoodle%2F$CURRENT_BRANCH%2Fazuredeploy.json
echo "Deploy to Azure URL: $DEPLOY_TO_AZURE_URL"

VISUALIZE_URL=http://armviz.io/#/?load=https%3A%2F%2Fraw.githubusercontent.com%2FAzure%2FMoodle%2F$CURRENT_BRANCH%2Fazuredeploy.json
echo "Visualize template URL: $VISUALIZE_URL"

# Check values in README.md

VALUE=$(sed -n -e 's/.*deploybutton.png)](\([^)]*\)).*/\1/p' README.md)
if [[ "$VALUE" = "$DEPLOY_TO_AZURE_URL" ]]
then
    echo "Deploy to Azure URL is set correctly"
else
    echo "!!!!! Deploy to Azure URL is not set correctly in README.md, it is currently:"
    echo $VALUE
fi

VALUE=$(sed -n -e 's/.*visualizebutton.png)](\([^)]*\)).*/\1/p' README.md)
if [[ "$VALUE" = "$VISUALIZE_URL" ]]
then
    echo "Visualize URL is set correctly"
else
    echo "!!!!! Visualize URL is not set correctly in README.md, it is currently:"
    echo $VALUE
fi

# Check values in azuredeploy.json

VALUE=$(sed -n -e 's/.*\"baseTemplateUrl\": \"\([^\"]*\)\",/\1/p' azuredeploy.json)
if [[ "$VALUE" = "$BASE_TEMPLATE_URL" ]]
then
    echo "baseTemplateURL is set correctly"
else
    echo "!!!!! baseTemplateURL is not set correctly, it is currently:"
    echo $VALUE
fi

VALUE=$(sed -n -e 's/.*\"scriptLocation\": \"\([^\"]*\)\",/\1/p' azuredeploy.json)
if [[ "$VALUE" = "$SCRIPT_LOCATION" ]]
then
    echo "scriptLocation is set correctly"
else
    echo "!!!!! scriptLocation is not set correctly, it is currently:"
    echo $VALUE
fi


================================================
FILE: etc/keyvault.sh
================================================
#!/bin/bash

# Based on https://github.com/Azure/azure-quickstart-templates/blob/master/201-vmss-ubuntu-web-ssl/keyvault.sh

#set -e

usage()
{
    echo usage: keyvault.sh '<keyvaultname> <resource group name> <location> <secretname> <certpemfile> <keypemfile> <cacertpemfile>'
    echo The cacertpem file is optional. The template will accept a self-signed cert and key.
}

creategroup()
{

    local group=$(az group show -g $rgname)  
    if [ -n "$group" ]; then    
        echo Resource Group $rgname already exists. Skipping creation.
    else
        # Create a resource group for the keyvault
        az group create -n $rgname -l $location
    fi
}

createkeyvault()
{

    az keyvault show -n $vaultname 2> /dev/null
    if [ $? -eq 0 ]
    then    
        echo Key Vault $vaultname already exists. Skipping creation.
    else   
        echo Creating Key Vault $vaultname.

        creategroup 
        # Create the key vault
        az keyvault create --name $vaultname --resource-group $rgname --location $location --enabled-for-template-deployment true --enabled-for-deployment true
    fi  
}

convertcert()
{
    local cert=$1
    local key=$2
    local pfxfile=$3
    local pass=$4

    echo Creating PFX $pfxfile
    openssl pkcs12 -export -out $pfxfile -inkey $key -in $cert -password pass:$pass 2> /dev/null
    if [ $? -eq 1 ]
    then
        echo problem converting $key and $cert to pfx
        exit 1
    fi    

    fingerprint=$(openssl x509 -in $cert -noout -fingerprint | cut -d= -f2 | sed 's/://g' )
}

convertcacert()
{
    local cert=$1
    local pfxfile=$2
    local pass=$3

    echo Creating PFX $pfxfile
    openssl pkcs12 -export -out $pfxfile -nokeys -in $cert -password pass:$pass 2> /dev/null
    if [ $? -eq 1 ]
    then
        echo problem converting $cert to pfx
        exit 1
    fi    

    fingerprint=$(openssl x509 -in $cert -noout -fingerprint | cut -d= -f2 | sed 's/://g' )
}

storesecret()
{
    local secretfile=$1
    local name=$2
    filecontentencoded=$( cat $secretfile | base64 $base64_unwrap )

json=$(cat << EOF
{
"data": "${filecontentencoded}",
"dataType" :"pfx",
"password": "${pwd}"
}
EOF
)

    jsonEncoded=$( echo $json | base64 $base64_unwrap )

    r=$(az keyvault secret set --vault-name $vaultname --name $name --value $jsonEncoded)
    if [ $? -eq 1 ]
    then
        echo problem storing secret $name in $vaultname 
        exit 1
    fi    

    id=$(az keyvault secret show --vault-name $vaultname --name $name --query id -o tsv)
    echo Secret ID is $id
}

# We need at least 6 parameters
if [ "$#" -lt 6 ]; then
    usage
    exit
fi

# The base64 command on OSX does not know about the -w parameter, but outputs unwrapped base64 by default
base64_unwrap="-w 0"
[[ $(uname) == "Darwin" ]] && base64_unwrap=""

vaultname=$1
rgname=$2
location=$3
secretname=$4
certfile=$5
keyfile=$6
cacertfile=$7

# Create a random password with 33 bytes of entropy
# I picked 33 so the last character will not be =
pwd=$(dd if=/dev/urandom bs=32 count=1 2>/dev/null | base64)

certpfxfile=${certfile%.*crt}.pfx
cacertpfxfile=${cacertfile%.*crt}.pfx
casecretname=ca$secretname

createkeyvault

# converting SSL cert to pfx
convertcert $certfile $keyfile $certpfxfile $pwd
certprint=$fingerprint
echo $certpfxfile fingerprint is $fingerprint
# storing pfx in keyvau
Download .txt
gitextract_zdgud6lr/

├── .gitignore
├── .jshintrc
├── .travis.yml
├── CONTRIBUTE.md
├── Gruntfile.js
├── LICENSE
├── LICENSE-DOCS
├── README.md
├── SECURITY.md
├── azuredeploy-large-ha.json
├── azuredeploy-maximal.json
├── azuredeploy-minimal.json
├── azuredeploy-small2mid-noha.json
├── azuredeploy.json
├── azuredeploy.parameters.json
├── docs/
│   ├── Cleanup.md
│   ├── Deploy.md
│   ├── Environment-Variables.md
│   ├── Get-Install-Data.md
│   ├── Manage.md
│   ├── Parameters.md
│   ├── Preparation.md
│   ├── SslCert.md
│   ├── Test.md
│   └── env.json
├── env.json
├── etc/
│   ├── changeBranchInURL.sh
│   ├── checkBaseUrls.sh
│   ├── keyvault.sh
│   ├── travis/
│   │   ├── Configuration.py
│   │   ├── DeploymentTester.py
│   │   └── __init__.py
│   ├── travis.py
│   └── updateDocsParametersMd.sh
├── images/
│   └── Moodle-Architecture-PremiumFiles.vsdx
├── loadtest/
│   ├── Azure_Login.md
│   ├── Deploy_Load_Test_VM.md
│   ├── README.md
│   ├── azuredeploy.parameters.loadtest.defaults.json
│   ├── loadtest.sh
│   ├── moodle-on-azure-test-course-1.mbz
│   ├── simple-test-1.jmx
│   ├── simple-test-2.jmx
│   ├── time-gated-exam-test-dist-slaves.jmx
│   └── time-gated-exam-test.jmx
├── managedApplication/
│   ├── Cleanup.md
│   ├── DeployMoodleManagedApp.md
│   ├── Environment.md
│   ├── PublishMoodleManagedApplication.md
│   ├── README.md
│   ├── createServiceCatlogUpdate.sh
│   ├── createUIDefinition.json
│   └── parameters-template.json
├── metadata.json
├── migration/
│   ├── azure-fileshare-sa-deploy.json
│   └── azuredeploy-migration.json
├── nested/
│   ├── appgw.json
│   ├── controller.json
│   ├── controllersetup.json
│   ├── db-mssql.json
│   ├── db-mysql.json
│   ├── db-mysqlflex.json
│   ├── db-postgres.json
│   ├── gluster.json
│   ├── glustervm.json
│   ├── glustervmsetup.json
│   ├── network-subnets.json
│   ├── network-vnet-ddos.json
│   ├── network-vnet-privateDnsZone.json
│   ├── network-vnet.json
│   ├── network.json
│   ├── nfs-ha-vm.json
│   ├── nfs-ha.json
│   ├── recoveryservices.json
│   ├── recoveryservicesEnlist.json
│   ├── redis.json
│   ├── search-azure.json
│   ├── search-elastic-config.json
│   ├── search-elastic.json
│   ├── storageAccount.json
│   ├── tika.json
│   ├── tikaconfig.json
│   ├── vmsetupparams.json
│   └── webvmss.json
├── package.json
└── scripts/
    ├── helper_functions.sh
    ├── install_elastic.sh
    ├── install_gluster.sh
    ├── install_moodle.sh
    ├── install_tika.sh
    ├── setup_nfs_ha.sh
    └── setup_webserver.sh
Download .txt
SYMBOL INDEX (23 symbols across 2 files)

FILE: etc/travis/Configuration.py
  class Configuration (line 8) | class Configuration:
    method __init__ (line 9) | def __init__(self):
    method identify_resource_group (line 22) | def identify_resource_group(self):
    method identify_ssh_key (line 28) | def identify_ssh_key(self):
    method generate_deployment_properties (line 35) | def generate_deployment_properties(self):
    method identify_artifacts_location (line 51) | def identify_artifacts_location(self):
    method identify_source_branch (line 57) | def identify_source_branch(self):
    method is_valid (line 63) | def is_valid(self):
    method should_run_full_ci (line 77) | def should_run_full_ci(self):

FILE: etc/travis/DeploymentTester.py
  class DeploymentTester (line 17) | class DeploymentTester:
    method elapsed (line 19) | def elapsed(since):
    method __init__ (line 24) | def __init__(self):
    method run (line 34) | def run(self):
    method check_configuration (line 54) | def check_configuration(self):
    method login (line 64) | def login(self):
    method create_resource_group (line 78) | def create_resource_group(self):
    method validate (line 84) | def validate(self):
    method deploy (line 99) | def deploy(self):
    method load_deployment_outputs (line 120) | def load_deployment_outputs(self, outputs):
    method moodle_smoke_test (line 126) | def moodle_smoke_test(self):
    method moodle_admin_login (line 141) | def moodle_admin_login(self):
    method moodle_admin_login_curl (line 149) | def moodle_admin_login_curl(self):
    method delete_resource_group (line 174) | def delete_resource_group(self):
Condensed preview — 92 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,247K chars).
[
  {
    "path": ".gitignore",
    "chars": 1372,
    "preview": "# Byte-compiled / optimized / DLL files\n__pycache__/\n*.py[cod]\n*$py.class\n\n# C extensions\n*.so\n\n# Distribution / packagi"
  },
  {
    "path": ".jshintrc",
    "chars": 490,
    "preview": "{\n    \"esnext\": true,\n    \"node\": true,\n    \"browser\": true,\n    \"nomen\": false,\n    \"bitwise\": true,\n    \"eqeqeq\": true"
  },
  {
    "path": ".travis.yml",
    "chars": 514,
    "preview": "dist: trusty\n\nlanguage: python\n\nnode_js: \"0.12\"\n\npython: \"3.5\"\n\ncache:\n  - directories: node_modules\n  - pip\n\nenv:\n  - P"
  },
  {
    "path": "CONTRIBUTE.md",
    "chars": 9246,
    "preview": "# Contributing to Moodle on Azure \n\nThe TL;DR version is:\n\n  * We are a community project\n  * We seek to make decisions "
  },
  {
    "path": "Gruntfile.js",
    "chars": 321,
    "preview": "var grunt = require('grunt');\nrequire('load-grunt-tasks')(grunt);\n\nvar templates = ['nested/*.json', 'managedApplication"
  },
  {
    "path": "LICENSE",
    "chars": 1090,
    "preview": "The MIT License (MIT)\nCopyright (c) Microsoft Corporation\n\nPermission is hereby granted, free of charge, to any person o"
  },
  {
    "path": "LICENSE-DOCS",
    "chars": 18647,
    "preview": "Attribution 4.0 International\n\n=======================================================================\n\nCreative Common"
  },
  {
    "path": "README.md",
    "chars": 26228,
    "preview": "\n# Deploy and Manage a Scalable Moodle Cluster on Azure\n\nThis repository contains guides and [Azure Resource Manager](ht"
  },
  {
    "path": "SECURITY.md",
    "chars": 2757,
    "preview": "<!-- BEGIN MICROSOFT SECURITY.MD V0.0.7 BLOCK -->\n\n## Security\n\nMicrosoft takes the security of our software products an"
  },
  {
    "path": "azuredeploy-large-ha.json",
    "chars": 3999,
    "preview": "{\n    \"$schema\": \"https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#\",\n    \"contentVersion\""
  },
  {
    "path": "azuredeploy-maximal.json",
    "chars": 3834,
    "preview": "{\n    \"$schema\": \"https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#\",\n    \"contentVersion\""
  },
  {
    "path": "azuredeploy-minimal.json",
    "chars": 3232,
    "preview": "{\n    \"$schema\": \"https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#\",\n    \"contentVersion\""
  },
  {
    "path": "azuredeploy-small2mid-noha.json",
    "chars": 2984,
    "preview": "{\n    \"$schema\": \"https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#\",\n    \"contentVersion\""
  },
  {
    "path": "azuredeploy.json",
    "chars": 67199,
    "preview": "{\n    \"$schema\": \"https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#\",\n    \"contentVersion\""
  },
  {
    "path": "azuredeploy.parameters.json",
    "chars": 376,
    "preview": "{\n    \"$schema\": \"https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#\",\n    \"contentVersio"
  },
  {
    "path": "docs/Cleanup.md",
    "chars": 754,
    "preview": "# Cleanup All Resources\n\nTo cleanup a Moodle deployment simply delete the Resource Group that\ncontains it. The commands "
  },
  {
    "path": "docs/Deploy.md",
    "chars": 7149,
    "preview": "# Deploy Autoscaling Moodle Stack to Azure\n\nAfter following the steps in this this document you with awill have a\nnew Mo"
  },
  {
    "path": "docs/Environment-Variables.md",
    "chars": 2869,
    "preview": "# Environment Variables\n\nIn order to configure our deployment and tools we'll set up some\nenvironment variables to ensur"
  },
  {
    "path": "docs/Get-Install-Data.md",
    "chars": 5904,
    "preview": "# Retrieve essential install details\n\nOnce a deployment has completed the ARM template will output some\nvalues that you "
  },
  {
    "path": "docs/Manage.md",
    "chars": 9398,
    "preview": "# Managing a Scalable Moodle Cluster in Azure\n\nThis document provides an overview of how to perform various\nmanagement t"
  },
  {
    "path": "docs/Parameters.md",
    "chars": 15291,
    "preview": "# Moodle on Azure Parameters\n\nOur goal with these templates is to make it as easy as possible to\ndeploy a Moodle on Azur"
  },
  {
    "path": "docs/Preparation.md",
    "chars": 2143,
    "preview": "# Environment Preparation\n\nThis document describes how to ensure your environment is configured\nfor working with Moodle "
  },
  {
    "path": "docs/SslCert.md",
    "chars": 4635,
    "preview": "# SSL Certificate Management\n\nA valid SSL (TLS) certificate should be used with your domain name for the Moodle\nsite to "
  },
  {
    "path": "docs/Test.md",
    "chars": 182,
    "preview": "# Test a Moodle Instance\n\n## Prerequisites\n\nIt is obviously necessary to have a [Moodle cluster up and running](./Deploy"
  },
  {
    "path": "docs/env.json",
    "chars": 140,
    "preview": "{\n    \"MOODLE_RG_NAME\": \"rgmoodlearm13tagging\",\n    \"MOODLE_RG_LOCATION\": \"canadacentral\",\n    \"MOODLE_DEPLOYMENT_NAME\":"
  },
  {
    "path": "env.json",
    "chars": 133,
    "preview": "{\n    \"MOODLE_RG_NAME\": \"rgmoodlearm12\",\n    \"MOODLE_RG_LOCATION\": \"canadacentral\",\n    \"MOODLE_DEPLOYMENT_NAME\": \"MainD"
  },
  {
    "path": "etc/changeBranchInURL.sh",
    "chars": 121,
    "preview": "#!/bin/bash\n\nfrom=$1\nto=$2\n\nsed -i s/%2F${from}%2F/%2F${to}%2F/g README.md\nsed -i s#/${from}/#/${to}/#g azuredeploy.json"
  },
  {
    "path": "etc/checkBaseUrls.sh",
    "chars": 2081,
    "preview": "# Ensure that the Base URL for templates, scripts and deploy to Azure buttons\n# is correctly set for the current git bra"
  },
  {
    "path": "etc/keyvault.sh",
    "chars": 4255,
    "preview": "#!/bin/bash\n\n# Based on https://github.com/Azure/azure-quickstart-templates/blob/master/201-vmss-ubuntu-web-ssl/keyvault"
  },
  {
    "path": "etc/travis/Configuration.py",
    "chars": 3051,
    "preview": "import json\nimport os\nimport time\n\nfrom azure.mgmt.resource.resources.v2017_05_10.models import DeploymentMode\n\n\nclass C"
  },
  {
    "path": "etc/travis/DeploymentTester.py",
    "chars": 7294,
    "preview": "import os\nimport pycurl\nimport sys\nimport tempfile\nimport time\nimport urllib\nfrom io import BytesIO\nfrom pycurl import C"
  },
  {
    "path": "etc/travis/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "etc/travis.py",
    "chars": 103,
    "preview": "#!/usr/bin/env python3\n\nfrom travis.DeploymentTester import DeploymentTester\n\nDeploymentTester().run()\n"
  },
  {
    "path": "etc/updateDocsParametersMd.sh",
    "chars": 493,
    "preview": "#!/bin/bash\n\n# Should be run at the git repo root as: $ ./etc/updateDocsParameters.sh\n\ndpkg -l jq &> /dev/null || sudo a"
  },
  {
    "path": "loadtest/Azure_Login.md",
    "chars": 783,
    "preview": "# Login to Azure\n\nBefore we start a load test session we need to first veriy that we are\nlogged in to Azure using the CL"
  },
  {
    "path": "loadtest/Deploy_Load_Test_VM.md",
    "chars": 3748,
    "preview": "# Setting up a test host\n\nTo run load tests using the resources in this directory, you'll want\nto spin up an Ubuntu VM ("
  },
  {
    "path": "loadtest/README.md",
    "chars": 6349,
    "preview": "# Load-Testing Deployed Moodle Cluster\n\nThis directory currently contains utility scripts, a Moodle test\ncourse, and an "
  },
  {
    "path": "loadtest/azuredeploy.parameters.loadtest.defaults.json",
    "chars": 811,
    "preview": "{\n    \"$schema\": \"https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#\",\n    \"contentVersio"
  },
  {
    "path": "loadtest/loadtest.sh",
    "chars": 15315,
    "preview": "#!/bin/bash\n\n# This is not fully tested. Just documenting what's needed.\nfunction install_java_and_jmeter\n{\n    sudo apt"
  },
  {
    "path": "loadtest/simple-test-1.jmx",
    "chars": 170825,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<jmeterTestPlan version=\"1.2\" properties=\"4.0\" jmeter=\"4.0 r1823414\">\n  <hashTree"
  },
  {
    "path": "loadtest/simple-test-2.jmx",
    "chars": 193606,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<jmeterTestPlan version=\"1.2\" properties=\"4.0\" jmeter=\"4.0 r1823414\">\n  <hashTree"
  },
  {
    "path": "loadtest/time-gated-exam-test-dist-slaves.jmx",
    "chars": 96521,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<jmeterTestPlan version=\"1.2\" properties=\"4.0\" jmeter=\"4.0 r1823414\">\n  <hashTree"
  },
  {
    "path": "loadtest/time-gated-exam-test.jmx",
    "chars": 95444,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<jmeterTestPlan version=\"1.2\" properties=\"4.0\" jmeter=\"4.0 r1823414\">\n  <hashTree"
  },
  {
    "path": "managedApplication/Cleanup.md",
    "chars": 1508,
    "preview": "# Cleaning up a Test Deployment\n\nIf you worked through the documentation in this section you will have\ncreated a nubmer "
  },
  {
    "path": "managedApplication/DeployMoodleManagedApp.md",
    "chars": 4112,
    "preview": "# Deploy a Moodle Based Managed Application into a Customer's Subscription\n\nIn this tutorial we'll demonstrate how your "
  },
  {
    "path": "managedApplication/Environment.md",
    "chars": 2221,
    "preview": "# Setup Environment\n\nFor convenience most of the configuration values we need to create and\nmanage our Moodle Managed Ap"
  },
  {
    "path": "managedApplication/PublishMoodleManagedApplication.md",
    "chars": 7384,
    "preview": "# Publish a Moodle Based Managed Appliction to Service Catalog\n\nIn this document we will look at how to publish a Moodle"
  },
  {
    "path": "managedApplication/README.md",
    "chars": 3396,
    "preview": "# Azure Managed Application\n\n[Azure Managed\nApplications](https://docs.microsoft.com/en-us/azure/managed-applications/ov"
  },
  {
    "path": "managedApplication/createServiceCatlogUpdate.sh",
    "chars": 4095,
    "preview": "# This script will create a Managed Application from the Azure/Moodle ARM template\n# see https://github.com/Azure/Moodle"
  },
  {
    "path": "managedApplication/createUIDefinition.json",
    "chars": 1233,
    "preview": "{\n    \"$schema\": \"https://schema.management.azure.com/schemas/0.1.2-preview/CreateUIDefinition.MultiVm.json#\",\n    \"hand"
  },
  {
    "path": "managedApplication/parameters-template.json",
    "chars": 313,
    "preview": "{\n    \"sshPublicKey\":         { \"value\": \"GEN-SSH-PUB-KEY\" },\n    \"redisDeploySwitch\":    { \"value\": false },\n    \"dbSer"
  },
  {
    "path": "metadata.json",
    "chars": 500,
    "preview": "{\n  \"itemDisplayName\": \"Autoscalable Moodle on Azure\",\n  \"description\": \"Deploys an autoscaling Moodle cluster with conf"
  },
  {
    "path": "migration/azure-fileshare-sa-deploy.json",
    "chars": 4029,
    "preview": "{\n    \"$schema\": \"https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#\",\n    \"contentVersion\""
  },
  {
    "path": "migration/azuredeploy-migration.json",
    "chars": 11284,
    "preview": "{\n    \"$schema\": \"https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#\",\n    \"contentVersion\""
  },
  {
    "path": "nested/appgw.json",
    "chars": 7484,
    "preview": "{\n    \"$schema\": \"http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json\",\n    \"contentVersion\": "
  },
  {
    "path": "nested/controller.json",
    "chars": 10658,
    "preview": "{\n    \"$schema\": \"http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#\",\n    \"contentVersion\":"
  },
  {
    "path": "nested/controllersetup.json",
    "chars": 3019,
    "preview": "{\n    \"$schema\": \"http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#\",\n    \"contentVersion\":"
  },
  {
    "path": "nested/db-mssql.json",
    "chars": 5145,
    "preview": "{\n    \"$schema\": \"http://schema.management.azure.com/schemas/2015-01-01-preview/deploymentTemplate.json#\",\n    \"contentV"
  },
  {
    "path": "nested/db-mysql.json",
    "chars": 6254,
    "preview": "{\n    \"$schema\": \"http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json#\",\n    \"contentV"
  },
  {
    "path": "nested/db-mysqlflex.json",
    "chars": 6278,
    "preview": "{\n    \"$schema\": \"http://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#\",\n    \"contentVersion\":"
  },
  {
    "path": "nested/db-postgres.json",
    "chars": 6344,
    "preview": "{\n    \"$schema\": \"http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json#\",\n    \"contentV"
  },
  {
    "path": "nested/gluster.json",
    "chars": 2869,
    "preview": "{\n    \"$schema\": \"https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#\",\n    \"contentVersion\""
  },
  {
    "path": "nested/glustervm.json",
    "chars": 8296,
    "preview": "{\n    \"$schema\": \"https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#\",\n    \"contentVersion\""
  },
  {
    "path": "nested/glustervmsetup.json",
    "chars": 2575,
    "preview": "{\n    \"$schema\": \"https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#\",\n    \"contentVersion\""
  },
  {
    "path": "nested/network-subnets.json",
    "chars": 6179,
    "preview": "{\n    \"$schema\": \"http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#\",\n    \"contentVersion\":"
  },
  {
    "path": "nested/network-vnet-ddos.json",
    "chars": 2095,
    "preview": "{\n    \"$schema\": \"http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#\",\n    \"contentVersion\":"
  },
  {
    "path": "nested/network-vnet-privateDnsZone.json",
    "chars": 1909,
    "preview": "{\n    \"$schema\": \"http://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#\",\n    \"contentVersion\":"
  },
  {
    "path": "nested/network-vnet.json",
    "chars": 970,
    "preview": "{\n    \"$schema\": \"http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#\",\n    \"contentVersion\":"
  },
  {
    "path": "nested/network.json",
    "chars": 25110,
    "preview": "{\n    \"$schema\": \"http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#\",\n    \"contentVersion\":"
  },
  {
    "path": "nested/nfs-ha-vm.json",
    "chars": 11056,
    "preview": "{\n    \"$schema\": \"https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#\",\n    \"contentVersion\""
  },
  {
    "path": "nested/nfs-ha.json",
    "chars": 25494,
    "preview": "{\n    \"$schema\": \"https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#\",\n    \"contentVersion\""
  },
  {
    "path": "nested/recoveryservices.json",
    "chars": 4148,
    "preview": "{\n    \"$schema\": \"http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#\",\n    \"contentVersion\":"
  },
  {
    "path": "nested/recoveryservicesEnlist.json",
    "chars": 1996,
    "preview": "{\n    \"$schema\": \"http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#\",\n    \"contentVersion\":"
  },
  {
    "path": "nested/redis.json",
    "chars": 1595,
    "preview": "{\n    \"$schema\": \"http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json\",\n    \"contentVersion\": "
  },
  {
    "path": "nested/search-azure.json",
    "chars": 2245,
    "preview": "{\n    \"$schema\": \"http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#\",\n    \"contentVersion\":"
  },
  {
    "path": "nested/search-elastic-config.json",
    "chars": 3948,
    "preview": "{\n    \"$schema\": \"http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#\",\n    \"contentVersion\":"
  },
  {
    "path": "nested/search-elastic.json",
    "chars": 17838,
    "preview": "{\n    \"$schema\": \"http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#\",\n    \"contentVersion\":"
  },
  {
    "path": "nested/storageAccount.json",
    "chars": 3862,
    "preview": "{\n    \"$schema\": \"https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#\",\n    \"contentVersion\""
  },
  {
    "path": "nested/tika.json",
    "chars": 6734,
    "preview": "{\n    \"$schema\": \"http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#\",\n    \"contentVersion\":"
  },
  {
    "path": "nested/tikaconfig.json",
    "chars": 1997,
    "preview": "{\n    \"$schema\": \"http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#\",\n    \"contentVersion\":"
  },
  {
    "path": "nested/vmsetupparams.json",
    "chars": 5875,
    "preview": "{\n    \"$schema\": \"http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#\",\n    \"contentVersion\":"
  },
  {
    "path": "nested/webvmss.json",
    "chars": 13602,
    "preview": "{\n    \"$schema\": \"https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#\",\n    \"contentVersion\""
  },
  {
    "path": "package.json",
    "chars": 576,
    "preview": "{\n  \"name\": \"azure-moodle\",\n  \"version\": \"1.0.0\",\n  \"description\": \"A package file for developer depenedencies when test"
  },
  {
    "path": "scripts/helper_functions.sh",
    "chars": 56114,
    "preview": "#!/bin/bash\n\n# Common functions definitions\n\nfunction wait_for_process {\n  until [ -z $(/usr/bin/pgrep ${1}) ]; do\n    p"
  },
  {
    "path": "scripts/install_elastic.sh",
    "chars": 5795,
    "preview": "#!/bin/bash\n# Custom Script for Linux\n\n# The MIT License (MIT)\n#\n# Permission is hereby granted, free of charge, to any "
  },
  {
    "path": "scripts/install_gluster.sh",
    "chars": 8809,
    "preview": "#!/bin/bash\n\n# This script built for Ubuntu Server 16.04 LTS\n# You can customize variables such as MOUNTPOINT, RAIDCHUNK"
  },
  {
    "path": "scripts/install_moodle.sh",
    "chars": 49003,
    "preview": "#!/bin/bash\n\n# The MIT License (MIT)\n#\n# Permission is hereby granted, free of charge, to any person obtaining a copy\n# "
  },
  {
    "path": "scripts/install_tika.sh",
    "chars": 3567,
    "preview": "#!/bin/bash\n# Custom Script for Linux\n\n# The MIT License (MIT)\n#\n# Permission is hereby granted, free of charge, to any "
  },
  {
    "path": "scripts/setup_nfs_ha.sh",
    "chars": 8437,
    "preview": "#!/bin/bash\n#\n# Script to set up highly available NFS server on an Ubuntu 16.04 (or higher) VM\n# that should be used on "
  },
  {
    "path": "scripts/setup_webserver.sh",
    "chars": 28235,
    "preview": "#!/bin/bash\n# Custom Script for Linux\n\n# The MIT License (MIT)\n#\n# Permission is hereby granted, free of charge, to any "
  }
]

// ... and 2 more files (download for full content)

About this extraction

This page contains the full source code of the Azure/Moodle GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 92 files (1.1 MB), approximately 258.8k tokens, and a symbol index with 23 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!