зеркало из https://github.com/Azure/missionlz.git
Родитель
08eae8edaf
Коммит
4ca3dd5924
|
@ -27,17 +27,17 @@ jobs:
|
|||
|
||||
- name: get vars
|
||||
run : |
|
||||
cd build
|
||||
cd src/build
|
||||
./get_vars.sh
|
||||
|
||||
- name: login
|
||||
run : |
|
||||
cd build
|
||||
cd src/build
|
||||
./login_azcli.sh vars/mlz_tf_cfg.var
|
||||
|
||||
- name: apply terraform
|
||||
run : |
|
||||
cd build
|
||||
cd src/build
|
||||
./apply_tf.sh \
|
||||
vars/mlz_tf_cfg.var \
|
||||
vars/globals.tfvars \
|
||||
|
@ -49,7 +49,7 @@ jobs:
|
|||
|
||||
- name: destroy terraform
|
||||
run : |
|
||||
cd build
|
||||
cd src/build
|
||||
./destroy_tf.sh \
|
||||
vars/mlz_tf_cfg.var \
|
||||
vars/globals.tfvars \
|
||||
|
|
|
@ -21,4 +21,4 @@ jobs:
|
|||
- shell: bash
|
||||
name: validate and lint terraform
|
||||
run: |
|
||||
build/validate_tf.sh
|
||||
src/build/validate_tf.sh
|
||||
|
|
|
@ -7,9 +7,15 @@
|
|||
*.tfstate.backup
|
||||
terraform-provider-azurerm_v*
|
||||
terraform-provider-random_v*
|
||||
*.terraform.lock.hcl
|
||||
|
||||
# Setup config variables file
|
||||
mlz_tf_cfg.var
|
||||
saca-hub.tfvars.json
|
||||
tier-0.tfvars.json
|
||||
tier-1.tfvars.json
|
||||
tier-2.tfvars.json
|
||||
globals.tfvars.json
|
||||
|
||||
# Bash artifacts
|
||||
*.vars
|
||||
|
@ -37,4 +43,7 @@ artifacts/
|
|||
|
||||
# Python Tools for Visual Studio (PTVS)
|
||||
__pycache__/
|
||||
*.pyc
|
||||
*.pyc
|
||||
**/.idea/
|
||||
**/config_output/
|
||||
**/exec_output
|
|
@ -0,0 +1,61 @@
|
|||
NOTICES
|
||||
|
||||
This repository incorporates material as listed below or described in the code.
|
||||
|
||||
Component: Bootstrap
|
||||
Bootstrap Reboot v4.5.3 (https://getbootstrap.com/)
|
||||
Copyright 2011-2020 The Bootstrap Authors
|
||||
Copyright 2011-2020 Twitter, Inc.
|
||||
Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
||||
Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2011-2021 Twitter, Inc.
|
||||
Copyright (c) 2011-2021 The Bootstrap Authors
|
||||
|
||||
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.
|
||||
|
||||
|
||||
Component: jQuery v3.5.1 (c) JS Foundation and other contributors
|
||||
https://jquery.org/license
|
||||
Note: The license text for jquery redirects to https://tldrlegal.com/license/mit-license#fulltext
|
||||
which is reproduced below, including placeholders for year and copyright holders.
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) <year> <copyright holders>
|
||||
|
||||
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.
|
156
README.md
156
README.md
|
@ -1,138 +1,76 @@
|
|||
# Mission LZ
|
||||
|
||||
Terraform resources to deploy Tier 0, 1, and 2, and the components of a [SACA hub](https://docs.microsoft.com/en-us/azure/azure-government/compliance/secure-azure-computing-architecture).
|
||||
Mission Landing Zone is a highly opinionated template which IT oversight organizations can use to create a cloud management system to deploy Azure environments for their teams. It addresses a narrowly scoped, specific need for an SCCA compliant hub and spoke infrastructure.
|
||||
|
||||
## Getting Started
|
||||
Mission LZ is:
|
||||
|
||||
1. Log in using the Azure CLI
|
||||
- Designed for US Gov mission customers
|
||||
- Implements [SCCA](https://docs.microsoft.com/en-us/azure/azure-government/compliance/secure-azure-computing-architecture) requirements following Microsoft's [SACA](https://aka.ms/saca) implementation guidance
|
||||
- Deployable in commercial, government, and air-gapped Azure clouds
|
||||
- A narrow scope for a specific common need
|
||||
- A simple solution with low configuration
|
||||
- Written in Terraform and Linux shell scripts
|
||||
|
||||
```BASH
|
||||
az login
|
||||
```
|
||||
Mission Landing Zone is the right solution when:
|
||||
|
||||
1. [Configure the Terraform Backend](#Configure-the-Terraform-Backend)
|
||||
1. [Set Terraform Configuration Variables](#Set-Terraform-Configuration-Variables)
|
||||
1. [Deploy Terraform Configuration](#Deploy-Terraform-Configuration)
|
||||
- A simple, secure, and scalable hub and spoke infrastructure is needed
|
||||
- Various teams need separate, secure cloud environments administered by a central IT team
|
||||
- There is a need to implement SCCA
|
||||
- Hosting any workload requiring a secure environment, for example: data warehousing, AI/ML, and containerized applications
|
||||
|
||||
### Configure the Terraform Backend
|
||||
Design goals include:
|
||||
|
||||
The MLZ deployment architecture uses a single Service Principal whose credentials are stored in a central "config" Key Vault. Terraform state storage is distributed into a separate storage account for each tier. When deploying the MLZ architecture, all tiers can be deployed into a single subscription or each tier can be deployed into its own subscription.
|
||||
- A simple, minimal set of code that is easy to configure
|
||||
- Good defaults that allow experimentation and testing in a single subscription
|
||||
- Deployment via command line or with a user interface
|
||||
- Uses Azure PaaS products
|
||||
|
||||
1. Create the `mlz_tf_cfg.var` file using the `mlz_tf_cfg.var.sample` as a template.
|
||||
Our intent is to enable IT Admins to use this software to:
|
||||
|
||||
The information in the `mlz_tf_cfg.var` file, will be used by `mlz_tf_setup.sh` to create and populate a `config.vars` file for each tier and saved inside the deployment folder for each tier (example: \src\core\tier-0\config.vars).
|
||||
- Test and evaluate the landing zone using a single Azure subscription
|
||||
- Develop a known good configuration that can be used for production with multiple Azure subscriptions
|
||||
- Optionally, customize the Terraform deployment configuration to suit specific needs
|
||||
- Deploy multiple customer workloads in production
|
||||
|
||||
For example:
|
||||
## Scope
|
||||
|
||||
```plaintext
|
||||
mlz_env_name="{MLZ_ENV_NAME}"
|
||||
mlz_config_location="{MLZ_CONFIG_LOCATION}"
|
||||
```
|
||||
Mission LZ has the following scope:
|
||||
|
||||
Would become:
|
||||
- Hub and spoke networking intended to comply with SCCA controls
|
||||
- Remote access
|
||||
- Shared services, i.e., services available to all workloads via the networking hub
|
||||
- Ability to create multiple workloads or team subscriptions
|
||||
- Compatibility with SCCA compliance (and other compliance frameworks)
|
||||
- Security using standard Azure tools with sensible defaults
|
||||
|
||||
```plaintext
|
||||
mlz_env_name="dev"
|
||||
mlz_config_location="eastus"
|
||||
```
|
||||
<!-- markdownlint-disable MD033 -->
|
||||
<!-- allow html for images so that they can be sized -->
|
||||
<img src="src/docs/images/scope.png" alt="Mission LZ Scope" width="600" />
|
||||
<!-- markdownlint-enable MD033 -->
|
||||
|
||||
1. Run `mlz_tf_setup.sh` at [src/scripts/mlz_tf_setup.sh](src/scripts/mlz_tf_setup.sh) to create:
|
||||
## Networking
|
||||
|
||||
- A config Resource Group to store the Key Vault
|
||||
- Resource Groups for each tier to store the Terraform state Storage Account
|
||||
- A Service Principal to execute terraform commands
|
||||
- An Azure Key Vault to store the Service Principal's client ID and client secret
|
||||
- A Storage Account and Container for each tier to store tier Terraform state files
|
||||
- Tier specific Terraform backend config files
|
||||
Networking is set up in a hub and spoke design, separated by tiers: T0, T1, T2, and multiple T3s. Security can be configured to allow separation of duties between all tiers. Most customers will deploy each tier to a separate Azure subscription, but multiple subscriptions are not required.
|
||||
|
||||
```bash
|
||||
# usage mlz_tf_setup.sh: <mlz_tf_cfg.var path>
|
||||
<!-- markdownlint-disable MD033 -->
|
||||
<img src="src/docs/images/networking.png" alt="Mission LZ Networking" width="600" />
|
||||
<!-- markdownlint-enable MD033 -->
|
||||
|
||||
chmod u+x src/scripts/mlz_tf_setup.sh
|
||||
## Getting Started using Mission LZ
|
||||
|
||||
src/scripts/mlz_tf_setup.sh src/core/mlz_tf_cfg.var
|
||||
```
|
||||
See our [Getting Started Guide](src/docs/getting-started.md) in the docs.
|
||||
|
||||
### Set Terraform Configuration Variables
|
||||
## Product Roadmap
|
||||
|
||||
First, clone the *.tfvars.sample file for the global Terraform configuration (e.g. [src/core/globals.tfvars.sample](src/core/globals.tfvars.sample)) and substitute placeholders marked by curly braces "{" and "}" with the values of your choosing.
|
||||
|
||||
Then, repeat this process, cloning the *.tfvars.sample file for the Terraform configuration(s) you are deploying and substitute placeholders marked by curly braces "{" and "}" with the values of your choosing.
|
||||
|
||||
For example:
|
||||
|
||||
```plaintext
|
||||
location="{MLZ_LOCATION}" # the templated value in src/core/globals.tfvars.sample
|
||||
```
|
||||
|
||||
Would become:
|
||||
|
||||
```plaintext
|
||||
location="eastus" # the value used by Terraform in src/core/globals.tfvars
|
||||
```
|
||||
|
||||
### Deploy Terraform Configuration
|
||||
|
||||
You can use `apply_terraform.sh` at [src/scripts/apply_terraform.sh](src/scripts/apply_terraform.sh) to both initialize Terraform and apply a Terraform configuration based on the backend environment variables and Terraform variables you've setup in previous steps.
|
||||
|
||||
The script `destroy_terraform.sh` at [src/scripts/destroy_terraform.sh](src/scripts/destroy_terraform.sh) is helpful during testing. This script is exactly like the
|
||||
`apply_terraform.sh` except it destroys resources defined in the target state file
|
||||
|
||||
`apply_terraform.sh` and `destroy_terraform.sh` take two arguments:
|
||||
|
||||
1. The Global variables file
|
||||
1. The directory that contains the main.tf and *.tfvars variables file of the configuration to apply
|
||||
|
||||
For example, from the root of this repository, you could apply Tier 0 with a command like:
|
||||
|
||||
```bash
|
||||
src/scripts/apply_terraform.sh \
|
||||
src/core/globals.tfvars \
|
||||
src/core/tier-0
|
||||
```
|
||||
|
||||
To apply Tier 1, you could then change the target directory:
|
||||
|
||||
```bash
|
||||
src/scripts/apply_terraform.sh \
|
||||
src/core/globals.tfvars \
|
||||
src/core/tier-1
|
||||
```
|
||||
|
||||
Repeating this same pattern, for whatever configuration you wanted to apply and reuse in some automated pipeline.
|
||||
|
||||
Use `init_terraform.sh` at [src/scripts/init_terraform.sh](src/scripts/init_terraform.sh) to perform just an initialization of the Terraform environment
|
||||
|
||||
To initialize Terraform for Tier 1, you could then change the target directory:
|
||||
|
||||
```bash
|
||||
src/scripts/init_terraform.sh \
|
||||
src/core/tier-1
|
||||
```
|
||||
|
||||
### Terraform Providers
|
||||
|
||||
The development container definition downloads the required Terraform plugin providers during the container build so that the container can be transported to an air-gapped network for use. The container also sets the `TF_PLUGIN_CACHE_DIR` environment variable, which Terraform uses as the search location for locally installed providers. If you are not using the container to deploy or if the `TF_PLUGIN_CACHE_DIR` environment variable is not set, Terraform will automatically attempt to download the provider from the internet when you execute the `terraform init` command.
|
||||
|
||||
See the development container [README](.devcontainer/README.md) for more details on building and running the container.
|
||||
|
||||
## Helpful Links
|
||||
|
||||
For more endpoint mappings between AzureCloud and AzureUsGovernment: <https://docs.microsoft.com/en-us/azure/azure-government/compare-azure-government-global-azure#guidance-for-developers/>
|
||||
See the [Projects](https://github.com/Azure/missionlz/projects) page for the release timeline and feature areas.
|
||||
|
||||
## Contributing
|
||||
|
||||
This project welcomes contributions and suggestions. 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.opensource.microsoft.com/>.
|
||||
This project welcomes contributions and suggestions. See our [Contributing Guide](CONTRIBUTING.md) for details.
|
||||
|
||||
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., status check, comment). Simply follow the instructions
|
||||
provided by the bot. You will only need to do this once across all repos using our CLA.
|
||||
## Feedback, Support, and How to Contact Us
|
||||
|
||||
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.
|
||||
Please see the [Support and Feedback Guide](SUPPORT.md). To report a security issue please see our [security guidance](./SECURITY.md).
|
||||
|
||||
## Trademarks
|
||||
|
||||
|
|
12
SUPPORT.md
12
SUPPORT.md
|
@ -1,12 +1,16 @@
|
|||
# Support
|
||||
# Support and Feedback
|
||||
|
||||
## How to file issues and get help
|
||||
|
||||
This project uses GitHub Issues to track bugs and feature requests. Please search the existing issues before filing new issues to avoid duplicates.
|
||||
We welcome feedback of all types.
|
||||
|
||||
For new issues, file your bug or feature request as a new [issue](https://github.com/Azure/missionlz/issues).
|
||||
This project uses GitHub issues to track bugs and feature requests. Please search the existing issues before filing new issues to avoid duplicates.
|
||||
|
||||
For help and questions about using this project, please submit a question as a new [issue](https://github.com/Azure/missionlz/issues) using the question template.
|
||||
For new issues, file your bug or feature request as a new [issue](https://github.com/Azure/missionlz/issues), [bug](https://github.com/Azure/missionlz/issues), or [feature request](https://github.com/Azure/missionlz/issues).
|
||||
|
||||
For help and questions about using this project, please submit a [question](https://github.com/Azure/missionlz/issues).
|
||||
|
||||
To report a security issue please see our [security guidance](./SECURITY.md).
|
||||
|
||||
## Microsoft Support Policy
|
||||
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
# Copyright (c) Microsoft Corporation.
|
||||
# Licensed under the MIT License.
|
||||
FROM ubuntu:20.04
|
||||
|
||||
# Instructs apt-get to run without a terminal
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
# Update distro (software-properties-common installs the add-apt-repository command)
|
||||
RUN apt-get update \
|
||||
&& apt-get -y install --no-install-recommends apt-utils software-properties-common 2>&1 \
|
||||
&& apt-get dist-upgrade -y \
|
||||
&& add-apt-repository ppa:git-core/ppa \
|
||||
&& apt-get install -y \
|
||||
unzip \
|
||||
wget \
|
||||
python3 \
|
||||
python3-pip
|
||||
|
||||
|
||||
# Install Terraform and Python
|
||||
RUN wget -O terraform.zip https://releases.hashicorp.com/terraform/0.14.3/terraform_0.14.3_linux_amd64.zip\
|
||||
&& unzip ./terraform.zip -d /usr/local/bin/ \
|
||||
&& rm terraform.zip
|
||||
|
||||
# Download Terraform providers (plugins)
|
||||
# Setting the TF_PLUGIN_CACHE_DIR environment variable instructs Terraform to search that folder for plugins first
|
||||
ENV TF_PLUGIN_CACHE_DIR=/usr/lib/tf-plugins
|
||||
ARG AZURERM_LOCAL_PATH="${TF_PLUGIN_CACHE_DIR}/registry.terraform.io/hashicorp/azurerm/2.50.0/linux_amd64"
|
||||
ARG RANDOM_LOCAL_PATH="${TF_PLUGIN_CACHE_DIR}/registry.terraform.io/hashicorp/random/3.1.0/linux_amd64"
|
||||
ARG AZURERM_PROVIDER=https://releases.hashicorp.com/terraform-provider-azurerm/2.50.0/terraform-provider-azurerm_2.50.0_linux_amd64.zip
|
||||
ARG RANDOM_PROVIDER=https://releases.hashicorp.com/terraform-provider-random/3.1.0/terraform-provider-random_3.1.0_linux_amd64.zip
|
||||
RUN wget -O azurerm.zip ${AZURERM_PROVIDER} \
|
||||
&& wget -O random.zip ${RANDOM_PROVIDER} \
|
||||
&& mkdir -p ${AZURERM_LOCAL_PATH} \
|
||||
&& mkdir -p ${RANDOM_LOCAL_PATH} \
|
||||
&& unzip azurerm.zip -d ${AZURERM_LOCAL_PATH} \
|
||||
&& unzip random.zip -d ${RANDOM_LOCAL_PATH} \
|
||||
&& rm azurerm.zip \
|
||||
&& rm random.zip
|
||||
|
||||
# Install the Microsoft package key
|
||||
RUN wget -q https://packages.microsoft.com/config/ubuntu/20.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb \
|
||||
&& dpkg -i packages-microsoft-prod.deb \
|
||||
&& rm packages-microsoft-prod.deb
|
||||
|
||||
# Install the Microsoft signing key
|
||||
RUN curl -sL https://packages.microsoft.com/keys/microsoft.asc | \
|
||||
gpg --dearmor | \
|
||||
tee /etc/apt/trusted.gpg.d/microsoft.gpg > /dev/null
|
||||
|
||||
# Install the AZ CLI repository
|
||||
RUN AZ_REPO=$(lsb_release -cs) \
|
||||
&& echo "deb [arch=amd64] https://packages.microsoft.com/repos/azure-cli/ $AZ_REPO main" | \
|
||||
tee /etc/apt/sources.list.d/azure-cli.list
|
||||
|
||||
# Install AZ CLI
|
||||
RUN apt-get update && apt-get install -y azure-cli
|
||||
|
||||
ADD ./front* /deployment/front/
|
||||
ADD ./scripts* /deployment/scripts/
|
||||
ADD ./core* /deployment/core/
|
||||
ADD ./modules* /deployment/modules/
|
||||
ADD ./build* /deployment/build/
|
||||
|
||||
RUN pip3 install -r /deployment/front/requirements.txt
|
||||
|
||||
|
||||
# Reset to the default value
|
||||
ENV DEBIAN_FRONTEND=dialog
|
||||
|
||||
WORKDIR /deployment/front
|
||||
|
||||
ENTRYPOINT python3 main.py
|
||||
|
|
@ -35,8 +35,8 @@ tier2_vars=$6
|
|||
display_tf_output=${7:-n}
|
||||
|
||||
# reference paths
|
||||
core_path=$(realpath ../src/core/)
|
||||
scripts_path=$(realpath ../src/scripts/)
|
||||
core_path=$(realpath ../core/)
|
||||
scripts_path=$(realpath ../scripts/)
|
||||
|
||||
# apply function
|
||||
apply() {
|
||||
|
@ -63,7 +63,7 @@ apply() {
|
|||
cp "${input_vars}" "${temp_vars}"
|
||||
|
||||
# remove any configuration tfvars and subtitute it with input vars
|
||||
tf_vars="${path}/${name}.tfvars"
|
||||
tf_vars="${path}/$(basename "${vars}")"
|
||||
rm -f "${tf_vars}"
|
||||
touch "${tf_vars}"
|
||||
cp "${temp_vars}" "${tf_vars}"
|
||||
|
@ -80,8 +80,8 @@ apply() {
|
|||
attempts=1
|
||||
max_attempts=5
|
||||
|
||||
apply_command="${scripts_path}/apply_terraform.sh ${globals} ${path} y"
|
||||
destroy_command="${scripts_path}/destroy_terraform.sh ${globals} ${path} y"
|
||||
apply_command="${scripts_path}/apply_terraform.sh ${globals} ${path} ${tf_vars} y"
|
||||
destroy_command="${scripts_path}/destroy_terraform.sh ${globals} ${path} ${tf_vars} y"
|
||||
|
||||
if [[ $display_tf_output == "n" ]]; then
|
||||
apply_command+=" &>/dev/null"
|
|
@ -35,8 +35,8 @@ tier2_vars=$6
|
|||
display_tf_output=${7:-n}
|
||||
|
||||
# reference paths
|
||||
core_path=$(realpath ../src/core/)
|
||||
scripts_path=$(realpath ../src/scripts/)
|
||||
core_path=$(realpath ../core/)
|
||||
scripts_path=$(realpath ../scripts/)
|
||||
|
||||
# destroy function
|
||||
destroy() {
|
||||
|
@ -63,7 +63,7 @@ destroy() {
|
|||
cp "${input_vars}" "${temp_vars}"
|
||||
|
||||
# remove any configuration tfvars and subtitute it with input vars
|
||||
tf_vars="${path}/${name}.tfvars"
|
||||
tf_vars="${path}/$(basename "${vars}")"
|
||||
rm -f "${tf_vars}"
|
||||
touch "${tf_vars}"
|
||||
cp "${temp_vars}" "${tf_vars}"
|
||||
|
@ -80,7 +80,7 @@ destroy() {
|
|||
attempts=1
|
||||
max_attempts=5
|
||||
|
||||
destroy_command="${scripts_path}/destroy_terraform.sh ${globals} ${path} y"
|
||||
destroy_command="${scripts_path}/destroy_terraform.sh ${globals} ${path} ${tf_vars} y"
|
||||
|
||||
if [[ "$display_tf_output" == "n" ]]; then
|
||||
destroy_command+=" &>/dev/null"
|
|
@ -0,0 +1,44 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
# Copyright (c) Microsoft Corporation.
|
||||
# Licensed under the MIT License.
|
||||
#
|
||||
# shellcheck disable=SC1090,SC2154
|
||||
# SC1090: Can't follow non-constant source. These values come from an external file.
|
||||
# SC2154: "var is referenced but not assigned". These values come from an external file.
|
||||
#
|
||||
# Get the tenant ID from some MLZ configuration file and login using known Service Principal credentials
|
||||
|
||||
set -e
|
||||
|
||||
error_log() {
|
||||
echo "${1}" 1>&2;
|
||||
}
|
||||
|
||||
usage() {
|
||||
echo "front_wrapper.sh: This provides a wrapper to get around python shell execution issues, it combines login and apply_tf"
|
||||
error_log "usage: front_wrapper.sh <mlz config> <globals.tfvars> <saca.tfvars> <tier0.tfvars> <tier1.tfvars> <tier2.tfvars> <display terraform output (y/n)> <sp_app_id> <sp_secret_key>"
|
||||
}
|
||||
|
||||
if [[ "$#" -lt 6 ]]; then
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mlz_config=$1
|
||||
|
||||
# source the variables from MLZ config
|
||||
source "${mlz_config}"
|
||||
|
||||
sp_id=${8:-$MLZCLIENTID}
|
||||
sp_pw=${9:-$MLZCLIENTSECRET}
|
||||
|
||||
# login with known credentials
|
||||
az login --service-principal \
|
||||
--user "${sp_id}" \
|
||||
--password="${sp_pw}" \
|
||||
--tenant "${mlz_tenantid}" \
|
||||
--allow-no-subscriptions \
|
||||
--output none
|
||||
|
||||
. "${BASH_SOURCE%/*}/apply_tf.sh" "${1}" "${2}" "${3}" "${4}" "${5}" "${6}" "${7}"
|
|
@ -17,7 +17,7 @@ error_log() {
|
|||
|
||||
usage() {
|
||||
echo "login_azcli.sh: Get the tenant ID from some MLZ configuration file and login using known Service Principal credentials"
|
||||
error_log "usage: login_azcli.sh <mlz config>"
|
||||
error_log "usage: login_azcli.sh <mlz config> <SP_ID> <SP_PW>"
|
||||
}
|
||||
|
||||
if [[ "$#" -lt 1 ]]; then
|
||||
|
@ -30,10 +30,13 @@ mlz_config=$1
|
|||
# source the variables from MLZ config
|
||||
source "${mlz_config}"
|
||||
|
||||
sp_id=${2:-$MLZCLIENTID}
|
||||
sp_pw=${3:-$MLZCLIENTSECRET}
|
||||
|
||||
# login with known credentials
|
||||
az login --service-principal \
|
||||
--user "${MLZCLIENTID}" \
|
||||
--password="${MLZCLIENTSECRET}" \
|
||||
--user "${sp_id}" \
|
||||
--password="${sp_pw}" \
|
||||
--tenant "${mlz_tenantid}" \
|
||||
--allow-no-subscriptions \
|
||||
--output none
|
||||
--output json
|
|
@ -22,7 +22,7 @@ fi
|
|||
|
||||
full_path=$(realpath "${0}")
|
||||
repo_path=$(dirname "$(dirname "${full_path}")")
|
||||
core_path="${repo_path}/src/core"
|
||||
core_path="${repo_path}/core"
|
||||
|
||||
if [ -d "$core_path" ];
|
||||
then
|
|
@ -0,0 +1,41 @@
|
|||
{
|
||||
"globals": {
|
||||
"form": [
|
||||
{
|
||||
"varname": "tf_environment",
|
||||
"type": "text",
|
||||
"default_val": "env:TF_ENV",
|
||||
"description": "Terraform deployment Environment https://www.terraform.io/docs/language/settings/backends/azurerm.html#environment",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"varname": "mlz_cloud",
|
||||
"type": "text",
|
||||
"default_val": "AzureCloud",
|
||||
"description": "Azure cloud being deployed to, # e.g. 'AzureCloud' or 'AzureUSGovernment', etc",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"varname": "mlz_tenantid",
|
||||
"type": "text",
|
||||
"default_val": "env:TENANT_ID",
|
||||
"description": "Tenant ID where your subscriptions liv",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"varname": "mlz_metadatahost",
|
||||
"type": "text",
|
||||
"default_val": "management.azure.com",
|
||||
"description": "Host for azure metadata: e.g 'management.azure.com' or 'management.usgovcloudapi.net'",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"varname": "mlz_location",
|
||||
"type": "text",
|
||||
"default_val": "env:LOCATION",
|
||||
"description": "The location that you're deploying to.",
|
||||
"options": []
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"tf_environment": "{TF_ENVIRONMENT}",
|
||||
"mlz_cloud": "{MLZ_CLOUD}",
|
||||
"mlz_tenantid": "{MLZ_TENANTID}",
|
||||
"mlz_metadatahost": "{MLZ_METADATAHOST}",
|
||||
"mlz_location": "{MLZ_REGION}"
|
||||
}
|
|
@ -0,0 +1,127 @@
|
|||
{
|
||||
"saca-hub": {
|
||||
"form": [
|
||||
{
|
||||
"varname": "deploymentname",
|
||||
"type": "text",
|
||||
"default_val": "mlzci",
|
||||
"description": "A unique name for your terraform deployment",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"varname": "saca_subid",
|
||||
"type": "text",
|
||||
"default_val": "env:SUBSCRIPTION_ID",
|
||||
"description": "The subscription id where the SACA hub lives",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"varname": "saca_rgname",
|
||||
"type": "text",
|
||||
"default_val": "rg-eastus-mlz-sacaci",
|
||||
"description": "Resource group name",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"varname": "saca_vnetname",
|
||||
"type": "text",
|
||||
"default_val": "vn-eastus-mlz-sacaci",
|
||||
"description": "Virtual Network Name",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"varname": "saca_lawsname",
|
||||
"type": "text",
|
||||
"default_val": "laws-eastus-mlz-sacaci",
|
||||
"description": "Name for log analytic workspace",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"varname": "vnet_address_space",
|
||||
"type": "list",
|
||||
"default_val": [
|
||||
"10.0.100.0/24"
|
||||
],
|
||||
"description": "Virtual Network Address Space",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"varname": "tier0_rgname",
|
||||
"type": "text",
|
||||
"default_val": "rg-eastus-mlz-t0ci",
|
||||
"description": "Tier 0 resource group name",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"varname": "tier0_vnetname",
|
||||
"type": "text",
|
||||
"default_val": "vn-eastus-mlz-t0ci",
|
||||
"description": "Tier 0 virtual network name",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"varname": "tier1_rgname",
|
||||
"type": "text",
|
||||
"default_val": "rg-eastus-mlz-t1ci",
|
||||
"description": "Tier 1 resource group name",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"varname": "tier1_vnetname",
|
||||
"type": "text",
|
||||
"default_val": "vn-eastus-mlz-t1ci",
|
||||
"description": "Tier one virtual network name",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"varname": "tier2_rgname",
|
||||
"type": "text",
|
||||
"default_val": "rg-eastus-mlz-t1ci",
|
||||
"description": "Tier 2 resource group name",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"varname": "tier2_vnetname",
|
||||
"type": "text",
|
||||
"default_val": "vn-eastus-mlz-t2ci",
|
||||
"description": "Tier 2 virtual network name",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"varname": "firewall_address_space",
|
||||
"type": "text",
|
||||
"default_val": "10.0.100.0/26",
|
||||
"description": "Address space for the firewall",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"varname": "saca_fwname",
|
||||
"type": "text",
|
||||
"default_val": "DemoFirewall",
|
||||
"description": "Saca Firewall Name",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"varname": "firewall_ipconfig_name",
|
||||
"type": "text",
|
||||
"default_val": "FirewallIPConfiguration",
|
||||
"description": "Name for the firewall ipconfig",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"varname": "public_ip_name",
|
||||
"type": "text",
|
||||
"default_val": "FirewallPublicIP",
|
||||
"description": "Name for the Public IP",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"varname": "create_network_watcher",
|
||||
"type": "boolean",
|
||||
"default_val": false,
|
||||
"description": "Do you need to create a network watcher here?",
|
||||
"options": []
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"deploymentname": "{SACA_DEPLOYMENTNAME}",
|
||||
"saca_subid": "{SACA_SUBID}",
|
||||
"saca_rgname": "{SACA_RGNAME}",
|
||||
"saca_vnetname": "{SACA_VNETNAME}",
|
||||
"saca_lawsname": "{SACA_LAWSNAME}",
|
||||
"vnet_address_space": [
|
||||
"{SACA_VNETSPACE}"
|
||||
],
|
||||
"tier0_rgname": "{TIER0_RGNAME}",
|
||||
"tier0_vnetname": "{TIER0_VNETNAME}",
|
||||
"tier1_rgname": "{TIER1_RGNAME}",
|
||||
"tier1_vnetname": "{TIER1_VNETNAME}",
|
||||
"tier2_rgname": "{TIER2_RGNAME}",
|
||||
"tier2_vnetname": "{TIER2_VNETNAME}",
|
||||
"firewall_address_space": "{SACA_FWSPACE}",
|
||||
"saca_fwname": "{SACA_FWNAME}",
|
||||
"firewall_ipconfig_name": "{SACA_FWIPCONFIGNAME}",
|
||||
"public_ip_name": "{SACA_FWPIPNAME}",
|
||||
"create_network_watcher": false
|
||||
}
|
|
@ -67,7 +67,7 @@ module "t0-network" {
|
|||
location = azurerm_resource_group.t0.location
|
||||
resource_group_name = azurerm_resource_group.t0.name
|
||||
vnet_name = var.tier0_vnetname
|
||||
vnet_address_space = var.vnet_address_space
|
||||
vnet_address_space = var.tier0_vnet_address_space
|
||||
log_analytics_workspace_id = data.azurerm_log_analytics_workspace.hub.id
|
||||
|
||||
tags = {
|
||||
|
|
|
@ -0,0 +1,134 @@
|
|||
{
|
||||
"tier-0": {
|
||||
"str_maps": {
|
||||
"TIER0_SUBNETVM_NAME": "subnets.{TIER0_SUBNETVM_NAME}.name"
|
||||
},
|
||||
"form": [
|
||||
{
|
||||
"varname": "saca_subid",
|
||||
"type": "text",
|
||||
"default_val": "env:SUBSCRIPTION_ID",
|
||||
"description": "Saca Hub Subscription ID",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"varname": "saca_rgname",
|
||||
"type": "text",
|
||||
"default_val": "rg-eastus-mlz-sacaci",
|
||||
"description": "Saca Hub Resource Group Name",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"varname": "saca_vnetname",
|
||||
"type": "text",
|
||||
"default_val": "vn-eastus-mlz-sacaci",
|
||||
"description": "Saca Virtual Network Name",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"varname": "saca_fwname",
|
||||
"type": "text",
|
||||
"default_val": "DemoFirewallci",
|
||||
"description": "Saca Firewall Name",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"varname": "saca_lawsname",
|
||||
"type": "text",
|
||||
"default_val": "laws-eastus-mlz-sacaci",
|
||||
"description": "Saca Log Analytic Workspace Name",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"varname": "tier0_subid",
|
||||
"type": "text",
|
||||
"default_val": "env:SUBSCRIPTION_ID",
|
||||
"description": "Tier0 Subscription Id",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"varname": "tier0_rgname",
|
||||
"type": "text",
|
||||
"default_val": "rg-eastus-mlz-t0ci",
|
||||
"description": "Tier0 Resource Group Name",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"varname": "tier0_vnetname",
|
||||
"type": "text",
|
||||
"default_val": "vn-eastus-mlz-t0ci",
|
||||
"description": "Tier0 Virtual Network Name",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"varname": "tier0_vnet_address_space",
|
||||
"type": "list",
|
||||
"default_val": [
|
||||
"10.0.110.0/26"
|
||||
],
|
||||
"description": "A list of values (NewLine Separated) for vnet address space",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"varname": "tier0_create_network_watcher",
|
||||
"type": "boolean",
|
||||
"default_val": false,
|
||||
"description": "Whether to create the network watcher in this tier vnet.",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"varname": "subnets.{TIER0_SUBNETVM_NAME}.name",
|
||||
"type": "text",
|
||||
"default_val": "tier0vms",
|
||||
"description": "A unique name for the Tier0 Subnet",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"varname": "subnets.{TIER0_SUBNETVM_NAME}.address_prefixes",
|
||||
"type": "list",
|
||||
"default_val": [
|
||||
"10.0.110.0/27"
|
||||
],
|
||||
"description": "A list of values (NewLine Separated) for vnet address space",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"varname": "subnets.{TIER0_SUBNETVM_NAME}.service_endpoints",
|
||||
"type": "list",
|
||||
"default_val": [
|
||||
"Microsoft.Storage"
|
||||
],
|
||||
"description": "A list of values (NewLine Separated) for service endpoints",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"varname": "subnets.{TIER0_SUBNETVM_NAME}.enforce_private_link_endpoint_network_policies",
|
||||
"type": "boolean",
|
||||
"default_val": false,
|
||||
"description": "Enforce Private Link Endpoint Policies",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"varname": "subnets.{TIER0_SUBNETVM_NAME}.enforce_private_link_service_network_policies",
|
||||
"type": "boolean",
|
||||
"default_val": false,
|
||||
"description": "Enforce private link service network policies",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"varname": "subnets.{TIER0_SUBNETVM_NAME}.nsg_name",
|
||||
"type": "text",
|
||||
"default_val": "tier0vmsnsg",
|
||||
"description": "Unique name for Network Security Group",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"varname": "subnets.{TIER0_SUBNETVM_NAME}.routetable_name",
|
||||
"type": "text",
|
||||
"default_val": "tier0vmsrtci",
|
||||
"description": "Tier 0 Routeable Subnet Name",
|
||||
"options": []
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
{
|
||||
"deploymentname": "{TIER0_DEPLOYMENTNAME}",
|
||||
"saca_subid": "{SACA_SUBID}",
|
||||
"saca_rgname": "{SACA_RGNAME}",
|
||||
"saca_vnetname": "{SACA_VNETNAME}",
|
||||
"saca_fwname": "{SACA_FWNAME}",
|
||||
"saca_lawsname": "{SACA_LAWSNAME}",
|
||||
"tier0_subid": "{TIER0_SUBID}",
|
||||
"tier0_rgname": "{TIER0_RGNAME}",
|
||||
"tier0_vnetname": "{TIER0_VNETNAME}",
|
||||
"tier0_vnet_address_space": [
|
||||
"{TIER0_VNETSPACE}"
|
||||
],
|
||||
"subnets": {
|
||||
"{TIER0_SUBNETVM_NAME}": {
|
||||
"name": "{TIER0_SUBNETVM_NAME}",
|
||||
"address_prefixes": [
|
||||
"{TIER0_SUBNETVM_ADDRESSPREFIXLIST}"
|
||||
],
|
||||
"service_endpoints": [
|
||||
"{TIER0_SUBNETVM_SERVICEENDPOINTLIST}"
|
||||
],
|
||||
"enforce_private_link_endpoint_network_policies": false,
|
||||
"enforce_private_link_service_network_policies": false,
|
||||
"nsg_name": "{TIER0_SUBNETVM_NSGNAME}",
|
||||
"nsg_rules": {
|
||||
"allow_ssh": {
|
||||
"name": "allow_ssh",
|
||||
"priority": "100",
|
||||
"direction": "Inbound",
|
||||
"access": "Allow",
|
||||
"protocol": "Tcp",
|
||||
"source_port_range": "",
|
||||
"destination_port_range": "22",
|
||||
"source_address_prefix": "*",
|
||||
"destination_address_prefix": ""
|
||||
},
|
||||
"allow_rdp": {
|
||||
"name": "allow_rdp",
|
||||
"priority": "200",
|
||||
"direction": "Inbound",
|
||||
"access": "Allow",
|
||||
"protocol": "Tcp",
|
||||
"source_port_range": "",
|
||||
"destination_port_range": "3389",
|
||||
"source_address_prefix": "*",
|
||||
"destination_address_prefix": ""
|
||||
}
|
||||
},
|
||||
"routetable_name": "{TIER0_SUBNETVM_RTNAME}"
|
||||
}
|
||||
},
|
||||
"create_network_watcher": false
|
||||
}
|
|
@ -18,7 +18,7 @@ tier0_vnetname = "{TIER0_VNETNAME}"
|
|||
# Network configuration section
|
||||
#################################
|
||||
|
||||
vnet_address_space = ["{TIER0_VNETSPACE}"]
|
||||
tier0_vnet_address_space = ["{TIER0_VNETSPACE}"]
|
||||
|
||||
subnets = {
|
||||
"{TIER0_SUBNETVM_NAME}" = {
|
||||
|
|
|
@ -73,7 +73,7 @@ variable "tier0_vnetname" {
|
|||
#################################
|
||||
# Network configuration section
|
||||
#################################
|
||||
variable "vnet_address_space" {
|
||||
variable "tier0_vnet_address_space" {
|
||||
description = "Address space prefixes list of strings"
|
||||
type = list(string)
|
||||
}
|
||||
|
|
|
@ -67,7 +67,7 @@ module "t1-network" {
|
|||
location = azurerm_resource_group.t1.location
|
||||
resource_group_name = azurerm_resource_group.t1.name
|
||||
vnet_name = var.tier1_vnetname
|
||||
vnet_address_space = var.vnet_address_space
|
||||
vnet_address_space = var.tier1_vnet_address_space
|
||||
log_analytics_workspace_id = data.azurerm_log_analytics_workspace.hub.id
|
||||
|
||||
tags = {
|
||||
|
|
|
@ -0,0 +1,134 @@
|
|||
{
|
||||
"tier-1": {
|
||||
"str_maps": {
|
||||
"TIER1_SUBNETVM_NAME": "subnets.{TIER1_SUBNETVM_NAME}.name"
|
||||
},
|
||||
"form": [
|
||||
{
|
||||
"varname": "saca_subid",
|
||||
"type": "text",
|
||||
"default_val": "env:SUBSCRIPTION_ID",
|
||||
"description": "Saca Hub Subscription ID",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"varname": "saca_rgname",
|
||||
"type": "text",
|
||||
"default_val": "rg-eastus-mlz-sacaci",
|
||||
"description": "Saca Hub Resource Group Name",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"varname": "saca_vnetname",
|
||||
"type": "text",
|
||||
"default_val": "vn-eastus-mlz-sacaci",
|
||||
"description": "Saca Virtual Network Name",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"varname": "saca_fwname",
|
||||
"type": "text",
|
||||
"default_val": "DemoFirewallci",
|
||||
"description": "Saca Firewall Name",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"varname": "saca_lawsname",
|
||||
"type": "text",
|
||||
"default_val": "laws-eastus-mlz-sacaci",
|
||||
"description": "Saca Log Analytic Workspace Name",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"varname": "tier1_subid",
|
||||
"type": "text",
|
||||
"default_val": "env:SUBSCRIPTION_ID",
|
||||
"description": "Tier0 Subscription Id",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"varname": "tier1_rgname",
|
||||
"type": "text",
|
||||
"default_val": "rg-eastus-mlz-t1ci",
|
||||
"description": "Tier0 Resource Group Name",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"varname": "tier1_vnetname",
|
||||
"type": "text",
|
||||
"default_val": "vn-eastus-mlz-t1ci",
|
||||
"description": "Tier0 Virtual Network Name",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"varname": "tier1_vnet_address_space",
|
||||
"type": "list",
|
||||
"default_val": [
|
||||
"10.0.115.0/26"
|
||||
],
|
||||
"description": "A list of values (NewLine Separated) for vnet address space",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"varname": "tier1_create_network_watcher",
|
||||
"type": "boolean",
|
||||
"default_val": false,
|
||||
"description": "Whether to create the network watcher in this tier vnet.",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"varname": "subnets.{TIER1_SUBNETVM_NAME}.name",
|
||||
"type": "text",
|
||||
"default_val": "tier1vms",
|
||||
"description": "A unique name for the Tier0 Subnet",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"varname": "subnets.{TIER1_SUBNETVM_NAME}.address_prefixes",
|
||||
"type": "list",
|
||||
"default_val": [
|
||||
"10.0.115.0/27"
|
||||
],
|
||||
"description": "A list of values (NewLine Separated) for vnet address space",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"varname": "subnets.{TIER1_SUBNETVM_NAME}.service_endpoints",
|
||||
"type": "list",
|
||||
"default_val": [
|
||||
"Microsoft.Storage"
|
||||
],
|
||||
"description": "A list of values (NewLine Separated) for service endpoints",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"varname": "subnets.{TIER1_SUBNETVM_NAME}.enforce_private_link_endpoint_network_policies",
|
||||
"type": "boolean",
|
||||
"default_val": false,
|
||||
"description": "Enforce Private Link Endpoint Policies",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"varname": "subnets.{TIER1_SUBNETVM_NAME}.enforce_private_link_service_network_policies",
|
||||
"type": "boolean",
|
||||
"default_val": false,
|
||||
"description": "Enforce private link service network policies",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"varname": "subnets.{TIER1_SUBNETVM_NAME}.nsg_name",
|
||||
"type": "text",
|
||||
"default_val": "tier1vmsnsg",
|
||||
"description": "Unique name for Network Security Group",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"varname": "subnets.{TIER1_SUBNETVM_NAME}.routetable_name",
|
||||
"type": "text",
|
||||
"default_val": "tier1vmsrtci",
|
||||
"description": "Tier 0 Routeable Subnet Name",
|
||||
"options": []
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
{
|
||||
"deploymentname": "{TIER1_DEPLOYMENTNAME}",
|
||||
"saca_subid": "{SACA_SUBID}",
|
||||
"saca_rgname": "{SACA_RGNAME}",
|
||||
"saca_vnetname": "{SACA_VNETNAME}",
|
||||
"saca_fwname": "{SACA_FWNAME}",
|
||||
"saca_lawsname": "{SACA_LAWSNAME}",
|
||||
"tier1_subid": "{TIER1_SUBID}",
|
||||
"tier1_rgname": "{TIER1_RGNAME}",
|
||||
"tier1_vnetname": "{TIER1_VNETNAME}",
|
||||
"tier1_vnet_address_space": [
|
||||
"{TIER1_VNETSPACE}"
|
||||
],
|
||||
"subnets": {
|
||||
"{TIER1_SUBNETVM_NAME}": {
|
||||
"name": "{TIER1_SUBNETVM_NAME}",
|
||||
"address_prefixes": [
|
||||
"{TIER1_SUBNETVM_ADDRESSPREFIXLIST}"
|
||||
],
|
||||
"service_endpoints": [
|
||||
"{TIER1_SUBNETVM_SERVICEENDPOINTLIST}"
|
||||
],
|
||||
"enforce_private_link_endpoint_network_policies": false,
|
||||
"enforce_private_link_service_network_policies": false,
|
||||
"nsg_name": "{TIER1_SUBNETVM_NSGNAME}",
|
||||
"nsg_rules": {
|
||||
"allow_ssh": {
|
||||
"name": "allow_ssh",
|
||||
"priority": "100",
|
||||
"direction": "Inbound",
|
||||
"access": "Allow",
|
||||
"protocol": "Tcp",
|
||||
"source_port_range": "",
|
||||
"destination_port_range": "22",
|
||||
"source_address_prefix": "*",
|
||||
"destination_address_prefix": ""
|
||||
},
|
||||
"allow_rdp": {
|
||||
"name": "allow_rdp",
|
||||
"priority": "200",
|
||||
"direction": "Inbound",
|
||||
"access": "Allow",
|
||||
"protocol": "Tcp",
|
||||
"source_port_range": "",
|
||||
"destination_port_range": "3389",
|
||||
"source_address_prefix": "*",
|
||||
"destination_address_prefix": ""
|
||||
}
|
||||
},
|
||||
"routetable_name": "{TIER1_SUBNETVM_RTNAME}"
|
||||
}
|
||||
},
|
||||
"create_network_watcher": false
|
||||
}
|
|
@ -18,7 +18,7 @@ tier1_vnetname = "{TIER1_VNETNAME}"
|
|||
# Network configuration section
|
||||
#################################
|
||||
|
||||
vnet_address_space = ["{TIER1_VNETSPACE}"]
|
||||
tier1_vnet_address_space = ["{TIER1_VNETSPACE}"]
|
||||
|
||||
subnets = {
|
||||
"{TIER1_SUBNETVM_NAME}" = {
|
||||
|
|
|
@ -73,7 +73,7 @@ variable "tier1_vnetname" {
|
|||
#################################
|
||||
# Network configuration section
|
||||
#################################
|
||||
variable "vnet_address_space" {
|
||||
variable "tier1_vnet_address_space" {
|
||||
description = "Address space prefixes for the virtual network"
|
||||
type = list(string)
|
||||
}
|
||||
|
|
|
@ -67,7 +67,7 @@ module "t2-network" {
|
|||
location = azurerm_resource_group.t2.location
|
||||
resource_group_name = azurerm_resource_group.t2.name
|
||||
vnet_name = var.tier2_vnetname
|
||||
vnet_address_space = var.vnet_address_space
|
||||
vnet_address_space = var.tier2_vnet_address_space
|
||||
log_analytics_workspace_id = data.azurerm_log_analytics_workspace.hub.id
|
||||
|
||||
tags = {
|
||||
|
|
|
@ -0,0 +1,134 @@
|
|||
{
|
||||
"tier-2": {
|
||||
"str_maps": {
|
||||
"TIER2_SUBNETVM_NAME": "subnets.{TIER2_SUBNETVM_NAME}.name"
|
||||
},
|
||||
"form": [
|
||||
{
|
||||
"varname": "saca_subid",
|
||||
"type": "text",
|
||||
"default_val": "env:SUBSCRIPTION_ID",
|
||||
"description": "Saca Hub Subscription ID",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"varname": "saca_rgname",
|
||||
"type": "text",
|
||||
"default_val": "rg-eastus-mlz-sacaci",
|
||||
"description": "Saca Hub Resource Group Name",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"varname": "saca_vnetname",
|
||||
"type": "text",
|
||||
"default_val": "vn-eastus-mlz-sacaci",
|
||||
"description": "Saca Virtual Network Name",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"varname": "saca_fwname",
|
||||
"type": "text",
|
||||
"default_val": "DemoFirewallci",
|
||||
"description": "Saca Firewall Name",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"varname": "saca_lawsname",
|
||||
"type": "text",
|
||||
"default_val": "laws-eastus-mlz-sacaci",
|
||||
"description": "Saca Log Analytic Workspace Name",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"varname": "tier2_subid",
|
||||
"type": "text",
|
||||
"default_val": "env:SUBSCRIPTION_ID",
|
||||
"description": "Tier0 Subscription Id",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"varname": "tier2_rgname",
|
||||
"type": "text",
|
||||
"default_val": "rg-eastus-mlz-t2ci",
|
||||
"description": "Tier2 Resource Group Name",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"varname": "tier2_vnetname",
|
||||
"type": "text",
|
||||
"default_val": "vn-eastus-mlz-t2ci",
|
||||
"description": "Tier2 Virtual Network Name",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"varname": "tier2_vnet_address_space",
|
||||
"type": "list",
|
||||
"default_val": [
|
||||
"10.0.120.0/26"
|
||||
],
|
||||
"description": "A list of values (NewLine Separated) for vnet address space",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"varname": "tier2_create_network_watcher",
|
||||
"type": "boolean",
|
||||
"default_val": false,
|
||||
"description": "Whether to create the network watcher in this tier vnet.",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"varname": "subnets.{TIER2_SUBNETVM_NAME}.name",
|
||||
"type": "text",
|
||||
"default_val": "tier2vms",
|
||||
"description": "A unique name for the Tier0 Subnet",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"varname": "subnets.{TIER2_SUBNETVM_NAME}.address_prefixes",
|
||||
"type": "list",
|
||||
"default_val": [
|
||||
"10.0.120.0/27"
|
||||
],
|
||||
"description": "A list of values (NewLine Separated) for vnet address space",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"varname": "subnets.{TIER2_SUBNETVM_NAME}.service_endpoints",
|
||||
"type": "list",
|
||||
"default_val": [
|
||||
"Microsoft.Storage"
|
||||
],
|
||||
"description": "A list of values (NewLine Separated) for service endpoints",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"varname": "subnets.{TIER2_SUBNETVM_NAME}.enforce_private_link_endpoint_network_policies",
|
||||
"type": "boolean",
|
||||
"default_val": false,
|
||||
"description": "Enforce Private Link Endpoint Policies",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"varname": "subnets.{TIER2_SUBNETVM_NAME}.enforce_private_link_service_network_policies",
|
||||
"type": "boolean",
|
||||
"default_val": false,
|
||||
"description": "Enforce private link service network policies",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"varname": "subnets.{TIER2_SUBNETVM_NAME}.nsg_name",
|
||||
"type": "text",
|
||||
"default_val": "tier2vmsnsg",
|
||||
"description": "Unique name for Network Security Group",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"varname": "subnets.{TIER2_SUBNETVM_NAME}.routetable_name",
|
||||
"type": "text",
|
||||
"default_val": "tier2vmsrtci",
|
||||
"description": "Tier 0 Routeable Subnet Name",
|
||||
"options": []
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
{
|
||||
"deploymentname":"{TIER2_DEPLOYMENTNAME}",
|
||||
"saca_subid":"{SACA_SUBID}",
|
||||
"saca_rgname":"{SACA_RGNAME}",
|
||||
"saca_vnetname":"{SACA_VNETNAME}",
|
||||
"saca_fwname":"{SACA_FWNAME}",
|
||||
"saca_lawsname":"{SACA_LAWSNAME}",
|
||||
"tier2_subid":"{TIER2_SUBID}",
|
||||
"tier2_rgname":"{TIER2_RGNAME}",
|
||||
"tier2_vnetname":"{TIER2_VNETNAME}",
|
||||
"tier2_vnet_address_space":["{TIER2_VNETSPACE}"],
|
||||
"subnets": {
|
||||
"{TIER2_SUBNETVM_NAME}": {
|
||||
"name": "{TIER2_SUBNETVM_NAME}",
|
||||
"address_prefixes": [
|
||||
"{TIER2_SUBNETVM_ADDRESSPREFIXLIST}"
|
||||
],
|
||||
"service_endpoints": [
|
||||
"{TIER2_SUBNETVM_SERVICEENDPOINTLIST}"
|
||||
],
|
||||
"enforce_private_link_endpoint_network_policies": false,
|
||||
"enforce_private_link_service_network_policies": false,
|
||||
"nsg_name": "{TIER2_SUBNETVM_NSGNAME}",
|
||||
"nsg_rules": {
|
||||
"allow_ssh": {
|
||||
"name": "allow_ssh",
|
||||
"priority": "100",
|
||||
"direction": "Inbound",
|
||||
"access": "Allow",
|
||||
"protocol": "Tcp",
|
||||
"source_port_range": "",
|
||||
"destination_port_range": "22",
|
||||
"source_address_prefix": "*",
|
||||
"destination_address_prefix": ""
|
||||
},
|
||||
"allow_rdp": {
|
||||
"name": "allow_rdp",
|
||||
"priority": "200",
|
||||
"direction": "Inbound",
|
||||
"access": "Allow",
|
||||
"protocol": "Tcp",
|
||||
"source_port_range": "",
|
||||
"destination_port_range": "3389",
|
||||
"source_address_prefix": "*",
|
||||
"destination_address_prefix": ""
|
||||
}
|
||||
},
|
||||
"routetable_name": "{TIER2_SUBNETVM_RTNAME}"
|
||||
}
|
||||
},
|
||||
"create_network_watcher": false
|
||||
}
|
|
@ -18,7 +18,7 @@ tier2_vnetname = "{TIER2_VNETNAME}"
|
|||
# Network configuration section
|
||||
#################################
|
||||
|
||||
vnet_address_space = ["{TIER2_VNETSPACE}"]
|
||||
tier2_vnet_address_space = ["{TIER2_VNETSPACE}"]
|
||||
|
||||
subnets = {
|
||||
"{TIER2_SUBNETVM_NAME}" = {
|
||||
|
|
|
@ -73,7 +73,7 @@ variable "tier2_vnetname" {
|
|||
#################################
|
||||
# Network configuration section
|
||||
#################################
|
||||
variable "vnet_address_space" {
|
||||
variable "tier2_vnet_address_space" {
|
||||
description = "Address space prefixes list of strings"
|
||||
type = list(string)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
# Copyright (c) Microsoft Corporation.
|
||||
# Licensed under the MIT License.
|
||||
# This docker-compose file defines a container that runs the ez deploy application for Mission LZ.
|
||||
# To run the containers:
|
||||
# 1. Install Docker Desktop or Docker CE
|
||||
# 2. In BASH or PowerShell, navigate to the folder containing this file.
|
||||
# 3. Run this command: docker-compose up -d
|
||||
version: '3.8'
|
||||
services:
|
||||
|
||||
webfront:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
container_name: "mlzfront"
|
||||
ports:
|
||||
- "80:80"
|
||||
|
|
@ -0,0 +1,119 @@
|
|||
# Command-Line Deployment
|
||||
|
||||
## Step-by-step
|
||||
|
||||
1. Log in using the Azure CLI
|
||||
|
||||
```BASH
|
||||
az login
|
||||
```
|
||||
|
||||
1. [Configure the Terraform Backend](#Configure-the-Terraform-Backend)
|
||||
1. [Set Terraform Configuration Variables](#Set-Terraform-Configuration-Variables)
|
||||
1. [Deploy Terraform Configuration](#Deploy-Terraform-Configuration)
|
||||
|
||||
### Configure the Terraform Backend
|
||||
|
||||
The MLZ deployment architecture uses a single Service Principal whose credentials are stored in a central "config" Key Vault. Terraform state storage is distributed into a separate storage account for each tier. When deploying the MLZ architecture, all tiers can be deployed into a single subscription or each tier can be deployed into its own subscription.
|
||||
|
||||
1. Create the `mlz_tf_cfg.var` file using the `mlz_tf_cfg.var.sample` as a template.
|
||||
|
||||
The information in the `mlz_tf_cfg.var` file, will be used by `mlz_tf_setup.sh` to create and populate a `config.vars` file for each tier and saved inside the deployment folder for each tier (example: \src\core\tier-0\config.vars).
|
||||
|
||||
For example:
|
||||
|
||||
```plaintext
|
||||
mlz_env_name="{MLZ_ENV_NAME}"
|
||||
mlz_config_location="{MLZ_CONFIG_LOCATION}"
|
||||
```
|
||||
|
||||
Would become:
|
||||
|
||||
```plaintext
|
||||
mlz_env_name="dev"
|
||||
mlz_config_location="eastus"
|
||||
```
|
||||
|
||||
1. Run `mlz_tf_setup.sh` at [src/scripts/mlz_tf_setup.sh](/src/scripts/mlz_tf_setup.sh) to create:
|
||||
|
||||
- A config Resource Group to store the Key Vault
|
||||
- Resource Groups for each tier to store the Terraform state Storage Account
|
||||
- A Service Principal to execute terraform commands
|
||||
- An Azure Key Vault to store the Service Principal's client ID and client secret
|
||||
- A Storage Account and Container for each tier to store tier Terraform state files
|
||||
- Tier specific Terraform backend config files
|
||||
|
||||
```bash
|
||||
# usage mlz_tf_setup.sh: <mlz_tf_cfg.var path>
|
||||
|
||||
chmod u+x src/scripts/mlz_tf_setup.sh
|
||||
|
||||
src/scripts/mlz_tf_setup.sh src/core/mlz_tf_cfg.var
|
||||
```
|
||||
|
||||
### Set Terraform Configuration Variables
|
||||
|
||||
First, clone the *.tfvars.sample file for the global Terraform configuration (e.g. [src/core/globals.tfvars.sample](/src/core/globals.tfvars.sample)) and substitute placeholders marked by curly braces "{" and "}" with the values of your choosing.
|
||||
|
||||
Then, repeat this process, cloning the *.tfvars.sample file for the Terraform configuration(s) you are deploying and substitute placeholders marked by curly braces "{" and "}" with the values of your choosing.
|
||||
|
||||
For example:
|
||||
|
||||
```plaintext
|
||||
location="{MLZ_LOCATION}" # the templated value in src/core/globals.tfvars.sample
|
||||
```
|
||||
|
||||
Would become:
|
||||
|
||||
```plaintext
|
||||
location="eastus" # the value used by Terraform in src/core/globals.tfvars
|
||||
```
|
||||
|
||||
### Deploy Terraform Configuration
|
||||
|
||||
You can use `apply_terraform.sh` at [src/scripts/apply_terraform.sh](/src/scripts/apply_terraform.sh) to both initialize Terraform and apply a Terraform configuration based on the backend environment variables and Terraform variables you've setup in previous steps.
|
||||
|
||||
The script `destroy_terraform.sh` at [src/scripts/destroy_terraform.sh](/src/scripts/destroy_terraform.sh) is helpful during testing. This script is exactly like the
|
||||
`apply_terraform.sh` except it destroys resources defined in the target state file
|
||||
|
||||
`apply_terraform.sh` and `destroy_terraform.sh` take two arguments:
|
||||
|
||||
1. The Global variables file
|
||||
1. The directory that contains the main.tf and *.tfvars variables file of the configuration to apply
|
||||
|
||||
For example, from the root of this repository, you could apply Tier 0 with a command like:
|
||||
|
||||
```bash
|
||||
src/scripts/apply_terraform.sh \
|
||||
src/core/globals.tfvars \
|
||||
src/core/tier-0
|
||||
```
|
||||
|
||||
To apply Tier 1, you could then change the target directory:
|
||||
|
||||
```bash
|
||||
src/scripts/apply_terraform.sh \
|
||||
src/core/globals.tfvars \
|
||||
src/core/tier-1
|
||||
```
|
||||
|
||||
Repeating this same pattern, for whatever configuration you wanted to apply and reuse in some automated pipeline.
|
||||
|
||||
Use `init_terraform.sh` at [src/scripts/init_terraform.sh](/src/scripts/init_terraform.sh) to perform just an initialization of the Terraform environment
|
||||
|
||||
To initialize Terraform for Tier 1, you could then change the target directory:
|
||||
|
||||
```bash
|
||||
src/scripts/init_terraform.sh \
|
||||
src/core/tier-1
|
||||
```
|
||||
|
||||
### Terraform Providers
|
||||
|
||||
The development container definition downloads the required Terraform plugin providers during the container build so that the container can be transported to an air-gapped network for use. The container also sets the `TF_PLUGIN_CACHE_DIR` environment variable, which Terraform uses as the search location for locally installed providers. If you are not using the container to deploy or if the `TF_PLUGIN_CACHE_DIR` environment variable is not set, Terraform will automatically attempt to download the provider from the internet when you execute the `terraform init` command.
|
||||
|
||||
See the development container [README](/.devcontainer/README.md) for more details on building and running the container.
|
||||
|
||||
## Helpful Links
|
||||
|
||||
For more endpoint mappings between AzureCloud and AzureUsGovernment: <https://docs.microsoft.com/en-us/azure/azure-government/compare-azure-government-global-azure#guidance-for-developers/>
|
|
@ -0,0 +1,33 @@
|
|||
# Getting Started
|
||||
|
||||
## Concepts
|
||||
|
||||
### Command Line or User Interface (UI) Deployments
|
||||
|
||||
You can deploy Mission LZ from your workstation, or from the Mission LZ user interface deployed and hosted in your Azure subscription. Some other configurations are possible, but these are the two simplest paths.
|
||||
|
||||
The command-line deployments involve (1) defining configuration settings (we have examples and defaults to make this easier), and (2) running shell scripts that consolidate and wrap the Terraform commands. We strongly recommend using these scripts because they were designed to be usable, but you could also run the Terraform templates directly using the `terraform` command line tool.
|
||||
|
||||
The user interface deployment runs in a container in your Azure subscription. There is a command that will build the container image on your machine, upload the container image to Azure, and run the container in Azure Container Instances (ACI). You browse to the user interface using a link generated by the UI deployment, log into the UI with your Azure Active Directory (AAD) credentials, fill out the configuration settings, and then press the button to deploy. The UI generates and runs the command-line deployment using your configuration settings.
|
||||
|
||||
### Use the Development Container for Command Line Deployments
|
||||
|
||||
If you are planning to deploy from your local workstation, we recommend using the VS Code development container specified in this repository. The container includes all the tools and pre-requisites, but you have to build and run the container. If you have Docker Desktop installed, then VS Code makes the rest of it easy. See the [README](../../.devcontainer/README.md) document in the `.devcontainer` folder for details.
|
||||
|
||||
If you want to deploy from the command line on your workstation but do not want to use the develompent container, take a look at the [`Dockerfile`](../../.devcontainer/Dockerfile) and the [`devcontainer.json`](../../.devcontainer/Dockerfile) file for examples on how to configure your environment.
|
||||
|
||||
The develoment container is not necessary if you want to use the Mission LZ user interface for deployments. Docker Desktop or Docker CE is still required to build the user interface container.
|
||||
|
||||
## Pre-Requisites
|
||||
|
||||
Operating system: Mac OS, Linux, or Windows 10 with Windows Subsystem for Linux (WSL) or a Linux virtual machine. (We developed this on Windows 10/WSL running Ubuntu 20.04.)
|
||||
|
||||
Docker: Docker Desktop or Docker CE (We use Docker Desktop on Windows 10, integrated with WSL.)
|
||||
|
||||
The Azure CLI.
|
||||
|
||||
All other tools and resources are in the development container and in the user interface container. The simplest path is to deploy from one of these containers, but it is not required if you want to configure your own deployment environment. See the development container [README](../../.devcontainer/README.md) document in the `.devcontainer` folder for details, and the user interface [Dockerfile](../Dockerfile) for details on user interface pre-requisites.
|
||||
|
||||
## Step-by-step
|
||||
|
||||
See the detailed step-by-step guides for [Command Line Deployments](command-line-deployment.md) and [User Interface Deployments](ui-deployment.md).
|
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 38 KiB |
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 37 KiB |
|
@ -0,0 +1,134 @@
|
|||
# Mission LZ User Interface
|
||||
|
||||
The mission LZ front-end is designed to be a single stop for easily entering all of the configuration items that Terraform needs to deploy Mission LZ to a target set of subscriptions.
|
||||
|
||||
## General Requirements
|
||||
|
||||
In order to run this software, you'll need to install some requirements, regardless of the path you choose to take for execution. Follow the General Requirements, and then follow instructions for either remote or local installation
|
||||
|
||||
For any of the following options you will need docker on your machine. If you are pre-packaging and deploying on a target network, you will need docker locally installed on both your local internet connected machine, and your target machine. The below instructions might need to be found in your target environment to replicate.
|
||||
|
||||
1. Install [Install WSL2](https://docs.microsoft.com/en-us/windows/wsl/install-win10) and [Docker on Windows for WSL2](https://docs.microsoft.com/en-us/windows/wsl/tutorials/wsl-containers), or [Install Docker Linux](https://docs.docker.com/engine/install/ubuntu) (Docker-Compose is also required, and is intalled by default with Docker Desktop.)
|
||||
1. [Install the Azure CLI](https://docs.microsoft.com/en-us/cli/azure/install-azure-cli). Be sure to install the Azure CLI in your Linux or WSL environment.
|
||||
|
||||
> If you will be transferring this package to an air-gapped cloud, please run the pre-packaging requirements to build a package that's ready to be transferred. This will prepare a docker image with all requirements to run ezdeploy. This is necessary if you don't have access to an updated docker repo/pip repo in your target network. If you do have these, you can proceed with the installation as if installing to an internet connected Azure Cloud.
|
||||
|
||||
## Step-By-Step
|
||||
|
||||
[Step-by-Step Remote Installation/Execution](#Step-by-Step-Azure-Installation) (recommended)
|
||||
[Step-by-Step Local Installation/Execution](#Step-by-Step-Local-Installation) (more difficult)
|
||||
|
||||
To get started, you'll need to be running from a bash/zsh environment. If you are on a Windows machine you can use WSL2.
|
||||
|
||||
### Step-by-Step Azure Installation
|
||||
|
||||
This process will build the user interface container image on your workstation using Docker, upload the container image to your Azure subscription, and install an instance of the container in Azure Container Instances (ACI). You'll need to have Docker installed locally, as well as the Azure Bash CLI.
|
||||
|
||||
From the "src" directory
|
||||
|
||||
```BASH
|
||||
chmod u+x ./scripts/setup_ezdeploy.sh
|
||||
./script/setup_ezdeploy.sh -d build -s <subscription_id> -t <tenant_id> -l <location> -e <tf_env_name> -m <mlz_env_name> -p port -0 <saca_subscription_id> -1 <tier0_subscription_id> -2 <tier1_subscription_id> -3 <tier2_subscription_id>"
|
||||
```
|
||||
|
||||
The final results will include a URI that you can use to access the front end running in a remote azure container instance.
|
||||
|
||||
### Step-by-Step Local Installation
|
||||
|
||||
Running the user interface on your local workstation is not our recommended approach because it requires more setup, but it works.
|
||||
|
||||
1. [Install Python](#Install-Python)
|
||||
1. [Run the User Interface Locally](#Run-the-User-Interface-Locally)
|
||||
|
||||
### Install Python
|
||||
|
||||
Basic Installation Instructions for Ubuntu 20.04. You may need to find different instructions for other flavors of Linux.
|
||||
|
||||
```BASH
|
||||
apt-get update \
|
||||
&& apt-get install -y \
|
||||
python3 \
|
||||
python3-pip \
|
||||
&& ln -s /usr/bin/python3 /usr/bin/python \
|
||||
&& ln -s /usr/bin/pip3 /usr/bin/pip
|
||||
```
|
||||
|
||||
### Run the User Interface Locally
|
||||
|
||||
Before running locally, you must follow the instructions in the primary readme file for this repo. You must have terraform pre-requisites installed in order to execute from a local system. Local execution will also require your credentials to have access to the service principal credentials for this system to assume; meaning that you should perform:
|
||||
|
||||
```BASH
|
||||
az login
|
||||
```
|
||||
|
||||
prior to following the following instructions
|
||||
|
||||
1. Install and Source a Python Virtual Environment
|
||||
|
||||
```bash
|
||||
python3 -m venv /path/to/new/virtual/environment
|
||||
source /path/to/new/virtual/environment/bin/activate
|
||||
```
|
||||
|
||||
2. Install requirements via pip
|
||||
|
||||
```BASH
|
||||
pip install -r src/front/requirements.txt
|
||||
```
|
||||
|
||||
3. Run the installation scripts to deploy app requirements
|
||||
|
||||
You will need the following variables for the script:
|
||||
|
||||
subscription_id: is the subscription that will house all deployment artifacts: kv, storage, fe instance
|
||||
|
||||
tenant_id: the tenant_id where all of your subscriptions are located
|
||||
|
||||
tf_env_name: Please refer to [https://www.terraform.io/docs/language/settings/backends/azurerm.html#environment] for more information. (Defaults to Public)
|
||||
|
||||
mlz_env_name: Can be anything unique to your deployment/environment it is used to ensure unique entries for resources. (Defaults to mlzdeployment)
|
||||
|
||||
port: Default is 80, if you are running in WSL or otherwise can't bind to 80, use this flag to enter a port
|
||||
|
||||
Multiple Subscriptions:
|
||||
If you are running with multiple subscriptions, you'll need to use these flags with the setup command.
|
||||
|
||||
-0: SACA Hub Subscription ID
|
||||
-1: Tier 0 Subscription ID
|
||||
-2: Tier 1 Subscription ID
|
||||
-3: Tier 2 Subscription ID
|
||||
|
||||
```bash
|
||||
chmod u+x ./script/setup_ezdeploy.sh
|
||||
./script/setup_ezdeploy.sh -d local -s <subscription_id> -t <tenant_id> -l <location> -e <tf_env_name> -m <mlz_env_name> -p port p -0 <saca_subscription_id> -1 <tier0_subscription_id> -2 <tier1_subscription_id> -3 <tier2_subscription_id>"
|
||||
```
|
||||
|
||||
4. Invoke environment variables needed for login (These are returned after setup_ezdeploy.sh is run)
|
||||
|
||||
```powershell
|
||||
$env:CLIENT_ID="<CLIENT_ID>"
|
||||
$env:CLIENT_SECRET="<CLIENT_SECRET"
|
||||
$env:TENANT_ID="<TENANT_ID>"
|
||||
$env:LOCATION='<CLOUD_LOCATION>'
|
||||
$env:SUBSCRIPTION_ID='<SUBSCRIPTION_ID>'
|
||||
$env:TF_ENV='<TERRAFORM_ENVIRONMENT>'
|
||||
$env:MLZ_ENV='<ENVIRONMENT_NAME>'
|
||||
```
|
||||
|
||||
```bash
|
||||
export CLIENT_ID="<CLIENT_ID>"
|
||||
export CLIENT_SECRET="<CLIENT_SECRET"
|
||||
export TENANT_ID="<TENANT_ID>"
|
||||
export LOCATION='<CLOUD_LOCATION>'
|
||||
export SUBSCRIPTION_ID='<SUBSCRIPTION_ID>'
|
||||
export TF_ENV='<TERRAFORM_ENVIRONMENT>'
|
||||
export MLZ_ENV='<ENVIRONMENT_NAME>'
|
||||
```
|
||||
|
||||
5. Execute web server
|
||||
|
||||
```bash
|
||||
python main.py <port_if_not_80>
|
||||
```
|
||||
|
||||
You can then access the application by pointing your browser at "localhost".
|
|
@ -0,0 +1,75 @@
|
|||
# Copyright (c) Microsoft Corporation.
|
||||
# Licensed under the MIT License.
|
||||
# Login Code from Examples at: https://github.com/Azure-Samples/ms-identity-python-webapp
|
||||
import msal
|
||||
import os
|
||||
from starlette.requests import Request
|
||||
|
||||
# TODO: Change all below items to keyvault reads
|
||||
|
||||
CLIENT_ENV_ID = os.getenv("CLIENT_ID", "None")
|
||||
|
||||
CLIENT_ENV_SECRET = os.getenv("CLIENT_SECRET", "None")
|
||||
|
||||
AUTHORITY = "https://login.microsoftonline.com/" + os.getenv("TENANT_ID")
|
||||
|
||||
# You can find the proper permission names from this document
|
||||
# https://docs.microsoft.com/en-us/graph/permissions-reference
|
||||
SCOPE = ["User.ReadBasic.All"]
|
||||
|
||||
|
||||
def load_cache(request: Request):
|
||||
"""
|
||||
Process a cache that exists in the users cookies
|
||||
|
||||
:param request: request object sent to the calling functions body
|
||||
:return: Returns the processed cache from the users session
|
||||
"""
|
||||
cache = msal.SerializableTokenCache()
|
||||
cookie_cache = request.cookies.get("token_cache")
|
||||
if cookie_cache:
|
||||
cache.deserialize(cookie_cache)
|
||||
return cache
|
||||
|
||||
|
||||
def build_msal_app(cache=None, client_id=CLIENT_ENV_ID, authority=None, secret=CLIENT_ENV_SECRET):
|
||||
"""
|
||||
Build the MSAL application for providing authentication
|
||||
|
||||
:param cache: provides the cache from the stored user cookies
|
||||
:param client_id: The client ID of the AAD application being used for login
|
||||
:param authority: Azure Authority for the application
|
||||
:param secret: Client Secret of the AAd application being used for login
|
||||
:return: Returns the app block used to facilitate login
|
||||
"""
|
||||
return msal.ConfidentialClientApplication(
|
||||
client_id, authority=authority or AUTHORITY,
|
||||
client_credential=secret, token_cache=cache)
|
||||
|
||||
|
||||
def build_auth_code_flow(authority=None, scopes=None, redirect_uri=None, client_id=CLIENT_ENV_ID, secret=CLIENT_ENV_SECRET):
|
||||
"""
|
||||
Use the MSAL app to build a flow cache to facilitate logging in
|
||||
|
||||
:param authority: Azure Authority
|
||||
:param scopes: The scopes being requested for this authorization
|
||||
:param redirect_uri: The redirect URI to provide to the AAD login, must be a URI registered to the AAD app
|
||||
:param client_id: The client ID of the AAD application being used for login
|
||||
:param secret: Client Secret of the AAd application being used for login
|
||||
:return: Returns the app flow code block used to provide URL's for login
|
||||
"""
|
||||
return build_msal_app(client_id=client_id, authority=authority, secret=secret).initiate_auth_code_flow(
|
||||
scopes or [],
|
||||
redirect_uri=redirect_uri)
|
||||
|
||||
"""
|
||||
#TODO: purge when sure this won't be useful
|
||||
Unused Example from MSAL examples
|
||||
def get_token_from_cache(request: Request, scope=None):
|
||||
cache = load_cache(request.cookies.get("token_cache")) # This web app maintains one cache per session
|
||||
cca = build_msal_app(cache=cache)
|
||||
accounts = cca.get_accounts()
|
||||
if accounts: # So all account(s) belong to the current signed-in user
|
||||
result = cca.acquire_token_silent(scope, account=accounts[0])
|
||||
return result
|
||||
"""
|
|
@ -0,0 +1,108 @@
|
|||
# Copyright (c) Microsoft Corporation.
|
||||
# Licensed under the MIT License.
|
||||
# Provides a set of utility functions to be called from the primary API
|
||||
import os
|
||||
import json
|
||||
from dominate.tags import *
|
||||
from typing import Union
|
||||
|
||||
def dotted_write(prop_name: str, val: Union[int, str], target_dict: dict):
|
||||
"""
|
||||
Purpose: Function takes in a property value to be mapped that contains .'s in a string.
|
||||
These .'s are resolved to a position in the dictionary in which the value will be written.
|
||||
This function takes advantage of python's pass by value and embeds a value into an originating dictionary
|
||||
that will be re-jsonified and written back to it's location.
|
||||
|
||||
:prop_name: a property value from a front end map dict
|
||||
:val: the value to be written tot he target_dict
|
||||
:target_dict: the dictionary that the value will be written into
|
||||
"""
|
||||
if "." in prop_name:
|
||||
write_name = "target_dict"
|
||||
nest_test = json.loads(json.dumps(target_dict))
|
||||
keys = prop_name.split(".")
|
||||
failed_test = False
|
||||
for loc in keys:
|
||||
if loc in nest_test:
|
||||
write_name += '["'+loc+'"]'
|
||||
nest_test = nest_test[loc]
|
||||
else:
|
||||
failed_test = True
|
||||
if not failed_test:
|
||||
exec(write_name + " = val")
|
||||
else:
|
||||
if prop_name in target_dict:
|
||||
target_dict[prop_name] = val
|
||||
|
||||
|
||||
def find_config(dir_scan=['../core', '../modules'], extension=".front.json"):
|
||||
"""
|
||||
Purpose: Function takes a list of directory names. Performs an os.walk to find *.front.json files and returns a
|
||||
dictionary of file names and their contents.
|
||||
|
||||
If .orig is in place on the file, it's removed.
|
||||
|
||||
:dir_scan: the list of directories to be scanned.
|
||||
:extension: the extension to look for and return in the scan
|
||||
"""
|
||||
config_files = {}
|
||||
for config_dir in dir_scan:
|
||||
walk = os.walk(os.path.join(os.getcwd(), config_dir))
|
||||
for root, _, files in walk:
|
||||
for f_name in files:
|
||||
if extension in f_name:
|
||||
cur_file = os.path.join(root, f_name)
|
||||
config_files[cur_file.replace(".orig", "")] = json.load(open(cur_file))
|
||||
|
||||
return config_files
|
||||
|
||||
|
||||
def build_form(form_doc: dict):
|
||||
"""
|
||||
Purpose: Function takes in a json document that describes a form, and returns the resulting dominate based
|
||||
form to be appended to the front end UI
|
||||
|
||||
:form_doc: a dictionary derived from a loaded json
|
||||
"""
|
||||
doc_form = form(id="terraform_config", action="/execute", method="post")
|
||||
doc_tabs = ul(cls="nav nav-tabs", id="myTab", role="tablist")
|
||||
doc_panels = div(cls="tab-content")
|
||||
for f_name, doc in form_doc.items():
|
||||
for title, config in doc.items():
|
||||
append_str = ""
|
||||
if "saca" in title:
|
||||
append_str = " active"
|
||||
doc_tabs.add(li(a(title, href="#" + title, cls="nav-link" + append_str, data_toggle="tab"), cls="nav-item",
|
||||
role="presentation"))
|
||||
doc_panel = div(role="tabpanel", cls="tab-pane fade show custom-pane" + append_str, id=title)
|
||||
with doc_panel:
|
||||
for el_item in config["form"]:
|
||||
with div(cls="form-elements"):
|
||||
label(el_item["description"], cls="breadcrumb", label_for=el_item["varname"])
|
||||
with div(cls="input-group input-group-sm mb-3"):
|
||||
with div(cls="input-group-prepend"):
|
||||
# Process environment options
|
||||
if type(el_item["default_val"]) != bool:
|
||||
if "env:" in el_item["default_val"]:
|
||||
el_item["default_val"] = os.getenv(el_item["default_val"].replace("env:", ""), "")
|
||||
span(el_item["varname"], cls="input-group-text")
|
||||
if el_item["type"] == "text":
|
||||
input_(id=el_item["varname"], cls="form-control", value=el_item["default_val"], name=el_item["varname"])
|
||||
elif el_item["type"] == "list":
|
||||
textarea("\n".join(el_item["default_val"]), id=el_item["varname"], cls="form-control", name="listinput:"+el_item["varname"], rows="4", columns="25")
|
||||
elif el_item["type"] == "select":
|
||||
select((option(x, value=x) for x in el_item["options"]), cls="form-control",
|
||||
default=el_item["default_val"], name=el_item["varname"], id=el_item["varname"])
|
||||
elif el_item["type"] == "boolean":
|
||||
span(
|
||||
input_(type="checkbox", default=bool(el_item["default_val"]), name=el_item["varname"], id=el_item["varname"])
|
||||
, cls="input-group-text")
|
||||
doc_panels.add(doc_panel)
|
||||
|
||||
doc_form.add(doc_tabs)
|
||||
doc_form.add(doc_panels)
|
||||
|
||||
with doc_form:
|
||||
input_(value="Execute Terraform", type="submit")
|
||||
|
||||
return doc_form
|
|
@ -0,0 +1,395 @@
|
|||
# Copyright (c) Microsoft Corporation.
|
||||
# Licensed under the MIT License.
|
||||
import json
|
||||
from datetime import datetime, timedelta
|
||||
import dominate
|
||||
import uvicorn
|
||||
from azure.identity import DefaultAzureCredential
|
||||
from azure.keyvault.secrets import SecretClient
|
||||
from dominate.tags import *
|
||||
from fastapi import FastAPI
|
||||
from fastapi.responses import HTMLResponse, JSONResponse, RedirectResponse
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
from starlette.requests import Request
|
||||
from lib import auth
|
||||
from subprocess import call
|
||||
import asyncio
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
from lib.utils import *
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
app.mount("/static", StaticFiles(directory="static"), name="static")
|
||||
|
||||
# Setup keyvault accesses to gather keys
|
||||
keyVaultName = os.getenv("KEYVAULT_ID", None)
|
||||
|
||||
if keyVaultName:
|
||||
keyVaultUrl = "https://{}.vault.azure.net/".format(keyVaultName)
|
||||
|
||||
# This will use your Azure Managed Identity
|
||||
credential = DefaultAzureCredential()
|
||||
secret_client = SecretClient(
|
||||
vault_url=keyVaultUrl,
|
||||
credential=credential)
|
||||
|
||||
static_location = '/static/'
|
||||
exec_output = os.path.join(os.getcwd(), "exec_output", "exec.txt")
|
||||
|
||||
if not os.path.exists(os.path.join(os.getcwd(), "config_output")):
|
||||
os.mkdir(os.path.join(os.getcwd(), "config_output"))
|
||||
|
||||
if not os.path.exists(os.path.join(os.getcwd(), "exec_output")):
|
||||
os.mkdir(os.path.join(os.getcwd(), "exec_output"))
|
||||
|
||||
if not os.path.exists(exec_output):
|
||||
with open(exec_output, "w+") as f:
|
||||
f.write("")
|
||||
|
||||
|
||||
@app.get("/")
|
||||
async def home(request: Request):
|
||||
"""
|
||||
Primary landing section for the app
|
||||
|
||||
:param request: request object sent in the post body when accessing this API
|
||||
:return: Will return a rendered HTML page
|
||||
"""
|
||||
# Handle the rendering of the login url
|
||||
login_url = ""
|
||||
user = ""
|
||||
flow = request.cookies.get("flow")
|
||||
if not request.cookies.get("user"):
|
||||
if keyVaultName:
|
||||
flow = auth.build_auth_code_flow(client_id=secret_client.get_secret("login-app-clientid").value,
|
||||
secret=secret_client.get_secret("login-app-pwd").value, scopes=auth.SCOPE)
|
||||
else:
|
||||
flow = auth.build_auth_code_flow(scopes=auth.SCOPE)
|
||||
login_url = flow["auth_uri"]
|
||||
else:
|
||||
user = json.loads(request.cookies.get("user"))
|
||||
|
||||
# Modal Templates
|
||||
tenant_input_html = "Please enter the Tenant ID for the Azure Instance you are using. This is needed to set up" \
|
||||
"authentication. You can find this by typing \"Tenant\" into the Azure Portal for where you" \
|
||||
" intend to deploy your infrastructure." + \
|
||||
br().render() + \
|
||||
div(
|
||||
input_("Tenant ID", id="tenantId", cls="form-control", aria_label="Small",
|
||||
aria_describedby="inputGroup-sizing-sm"),
|
||||
div(
|
||||
button("Save Id", type="button", id="saveTenant",
|
||||
cls="btn btn-outline-secondary"),
|
||||
cls="input-group-append"),
|
||||
cls="input-group input-group-sm mb-3").render(pretty=False)
|
||||
|
||||
login_input_html = "Click the below button to initiate AAD authentication" + br().render() + div(
|
||||
a("Login", href=login_url),
|
||||
cls="input-group input-group-sm mb-3").render(pretty=False)
|
||||
|
||||
try:
|
||||
config = json.load(open("form_config.json"))
|
||||
except FileNotFoundError:
|
||||
config = None
|
||||
|
||||
# Initiate the HTML Document
|
||||
doc = dominate.document(title="Mission LZ")
|
||||
|
||||
# Set initial style sheet in HTML doc head
|
||||
with doc.head:
|
||||
link(rel='stylesheet', href=static_location + 'bootstrap.min.css')
|
||||
link(rel='stylesheet', href=static_location + 'custom.css')
|
||||
|
||||
# Fill in the single page app template with forms and sections
|
||||
with doc:
|
||||
# Case for each variable type in JSON definition
|
||||
with div(cls="navbar navbar-expand-lg fixed-top navbar-dark bg-primary"):
|
||||
with div(cls="container"):
|
||||
a("Mission LZ", cls="navbar-brand", href="#")
|
||||
with div(cls='text-right'):
|
||||
if user:
|
||||
a("Logout " + user["name"], href="/logout", cls="btn btn-outline-secondary")
|
||||
|
||||
with div(cls="container"):
|
||||
with div(cls="page-header"):
|
||||
if config:
|
||||
pass
|
||||
# Parse each config element in a switch to generate a section of forms for it
|
||||
else:
|
||||
build_form(find_config())
|
||||
|
||||
# Modal
|
||||
with div(cls="modal fade", data_backdrop="static", id="promptModal", tabindex="-1", role="dialog",
|
||||
aria_hidden="true"):
|
||||
with div(cls="modal-dialog modal-dialog-centered modal-lg", role="document"):
|
||||
with div(cls="modal-content"):
|
||||
with div(cls="modal-header"):
|
||||
h5("Modal Title", cls="modal-title")
|
||||
div("Modal Content", cls="modal-body")
|
||||
with div(cls="modal-footer"):
|
||||
button("Close", id="modBtn", type="button", cls="btn btn-secondary", data_dismiss="modal")
|
||||
|
||||
# Include the Javascript Files in the output HTML
|
||||
script(src=static_location + 'jquery-3.5.1.min.js')
|
||||
script(src=static_location + 'bootstrap.bundle.min.js')
|
||||
script(src=static_location + 'custom.js')
|
||||
|
||||
script().add_raw_string("""
|
||||
function promptLogin() {
|
||||
$('#promptModal').on('show.bs.modal', function (event) {
|
||||
var modal = $(this)
|
||||
modal.find('.modal-title').text('Login')
|
||||
modal.find('.modal-body').html('""" + login_input_html + """')
|
||||
modal.find('#modBtn').hide()
|
||||
})
|
||||
$('#promptModal').modal('show')
|
||||
}
|
||||
|
||||
var interval = null
|
||||
function retrieve_results() {
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
url: "/poll",
|
||||
success: function(msg){
|
||||
$("#terminal").text(msg);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function submitForm(){
|
||||
interval = setInterval(retrieve_results, 2000);
|
||||
}
|
||||
|
||||
$(document).ready(function(){
|
||||
$('#showTenant').click(function(){
|
||||
promptTenant()
|
||||
})
|
||||
|
||||
logged_in = readCookie("user")
|
||||
|
||||
if(!logged_in || $.trim(logged_in) == ""){
|
||||
promptLogin()
|
||||
}
|
||||
})
|
||||
|
||||
$(document).on('submit', '#terraform_config', function(e) {
|
||||
$('#promptModal').on('show.bs.modal', function (event) {
|
||||
var modal = $(this)
|
||||
modal.find('.modal-title').text('Polling execution results: (Box May Stay Blank for an extended amount of time)')
|
||||
modal.find('.modal-body').html('<pre id="terminal"></pre>')
|
||||
modal.find('#modBtn').show()
|
||||
modal.find('#modBtn').click(function(){
|
||||
clearInterval(interval)
|
||||
})
|
||||
})
|
||||
$('#promptModal').modal('show')
|
||||
$.ajax({
|
||||
url: $(this).attr('action'),
|
||||
type: $(this).attr('method'),
|
||||
data: $(this).serialize(),
|
||||
success: function(html) {
|
||||
submitForm()
|
||||
}
|
||||
});
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
$("input").change(function(){
|
||||
$("input[name="+ this.name +"]").val(this.value)
|
||||
})
|
||||
""")
|
||||
|
||||
response = HTMLResponse(content=str(doc), status_code=200)
|
||||
response.set_cookie("flow", json.dumps(flow), expires=3600)
|
||||
return response
|
||||
|
||||
|
||||
# Display currently cached creds
|
||||
@app.get("/creds")
|
||||
async def display_creds(request: Request):
|
||||
"""
|
||||
Process the body request for debugging purposes if there's a user issue with Azure
|
||||
|
||||
:param request: request object sent in the post body when accessing this API
|
||||
:return: Will return a json block with relevant debugging data for the user session
|
||||
"""
|
||||
result = request.cookies.get("flow")
|
||||
user = request.cookies.get("user")
|
||||
if keyVaultName:
|
||||
test_value = secret_client.get_secret("login-app-clientid")
|
||||
return JSONResponse({"creds": user, "flow": result, "test_val": test_value.value})
|
||||
|
||||
|
||||
# Process Logout
|
||||
@app.get("/logout")
|
||||
async def process_logout():
|
||||
"""
|
||||
Purge the login information from the users session/cookie data
|
||||
|
||||
:return: Redirect to main body
|
||||
"""
|
||||
# Simply destroy the cookies in this session and get rid of the creds, redirect to landing
|
||||
response = RedirectResponse("/") # Process the destruction from main app/test result
|
||||
response.delete_cookie("user")
|
||||
response.delete_cookie("flow")
|
||||
return response
|
||||
|
||||
|
||||
# API To Capture the redirect from Azure
|
||||
@app.get("/redirect")
|
||||
async def capture_redirect(request: Request):
|
||||
"""
|
||||
Process the request body that's returned from AAD. This will process the login items to acquire user in info
|
||||
|
||||
:param request: request object sent in the post body when accessing this API
|
||||
:return: Will either redirect the user back to the main page, or display an error
|
||||
"""
|
||||
try:
|
||||
cache = auth.load_cache(request)
|
||||
if keyVaultName:
|
||||
result = auth.build_msal_app(cache, client_id=secret_client.get_secret("login-app-clientid").value,
|
||||
secret=secret_client.get_secret(
|
||||
"login-app-pwd").value).acquire_token_by_auth_code_flow(
|
||||
dict(json.loads(request.cookies.get("flow"))), dict(request.query_params))
|
||||
else:
|
||||
result = auth.build_msal_app(cache).acquire_token_by_auth_code_flow(
|
||||
dict(json.loads(request.cookies.get("flow"))), dict(request.query_params))
|
||||
if "error" in result:
|
||||
response = JSONResponse({"status": "error", "result": result})
|
||||
else:
|
||||
# response = JSONResponse({"status": "success", "result": result})
|
||||
response = RedirectResponse("/")
|
||||
response.set_cookie("user", json.dumps(result.get("id_token_claims")), expires=3600)
|
||||
except ValueError as e:
|
||||
response = JSONResponse({"status": "error", "result": "Possible CSRF related error" + str(e)})
|
||||
return response
|
||||
|
||||
|
||||
# Execute processes the form values entered by the user and generates the TF JSON file
|
||||
@app.post("/execute")
|
||||
async def process_input(request: Request):
|
||||
"""
|
||||
Process the dynamic form that's posted to this API and perform the required processing
|
||||
Initiate an async job for executing terraform
|
||||
|
||||
:param request: request object sent in the post body when accessing this API
|
||||
:return: Will return success if all items are completed
|
||||
"""
|
||||
dynamic_form = await request.form()
|
||||
form_values = dict(dynamic_form)
|
||||
if not request.cookies.get("user") or not request.cookies.get("flow"):
|
||||
return JSONResponse(content={"status": "Error: User Not Logged In"}, status_code=200)
|
||||
|
||||
# Reload form configs:
|
||||
form_config = find_config()
|
||||
|
||||
# Load tfvars initial files
|
||||
tf_json = find_config(extension=".orig.tfvars.json")
|
||||
|
||||
# Create a map based on str maps in the form config files
|
||||
maps = {}
|
||||
for _, config_doc in form_config.items():
|
||||
for _, config in config_doc.items():
|
||||
if "str_maps" in config:
|
||||
for strm, smap in config["str_maps"].items():
|
||||
maps[strm] = form_values[smap]
|
||||
|
||||
# Evaluate maps across loaded jsons
|
||||
for f_name, doc in tf_json.items():
|
||||
temp_dump = json.dumps(doc)
|
||||
for strm, smap in maps.items():
|
||||
temp_dump = temp_dump.replace("{" + strm + "}", smap)
|
||||
tf_json[f_name] = json.loads(temp_dump)
|
||||
|
||||
# Process all form keys:
|
||||
form_dump = str(json.dumps(form_values))
|
||||
for key, smap in maps.items():
|
||||
form_dump = form_dump.replace("{" + key + "}", smap)
|
||||
form_values = json.loads(form_dump)
|
||||
|
||||
# Write the values to the correct locations in the memory loaded json files
|
||||
for key, value in form_values.items():
|
||||
if "listinput:" in key:
|
||||
value = value.split("\n")
|
||||
key = key.replace("listinput:", "")
|
||||
for _, doc in tf_json.items():
|
||||
# Process a list type value
|
||||
dotted_write(key, value, doc)
|
||||
|
||||
# Loop all open TF documents and write them out
|
||||
for f_name, doc in tf_json.items():
|
||||
json.dump(doc, open(os.path.join(os.getcwd(), "config_output", os.path.basename(f_name)), "w+"))
|
||||
|
||||
# Use what we know and write out the environment file:
|
||||
with open(os.path.join(os.getcwd(), "config_output", "mlz_tf_var_front"), "w+") as f:
|
||||
f.writelines("tf_environment=\"" + os.getenv("TF_ENV") + "\"\n" +
|
||||
"mlz_env_name=\"" + os.getenv("MLZ_ENV") + "\"\n" +
|
||||
"mlz_config_subid=\"" + os.getenv("SUBSCRIPTION_ID") + "\"\n" +
|
||||
"mlz_config_location=\"" + os.getenv("LOCATION") + "\"\n" +
|
||||
"mlz_tenantid=\"" + os.getenv("TENANT_ID") + "\"\n" +
|
||||
"mlz_tier0_subid=\"" + form_values["tier0_subid"] + "\"\n" +
|
||||
"mlz_tier1_subid=\"" + form_values["tier1_subid"] + "\"\n" +
|
||||
"mlz_tier2_subid=\"" + form_values["tier2_subid"] + "\"\n" +
|
||||
"mlz_saca_subid=\"" + form_values["saca_subid"] + "\"\n")
|
||||
|
||||
# Call the build script
|
||||
# Check that it's executable:
|
||||
config_executable = os.path.join(os.getcwd(), "..", "scripts", "mlz_tf_setup.sh")
|
||||
apply_executable = os.path.join(os.getcwd(), "..", "build", "front_wrapper.sh")
|
||||
os.chmod(config_executable, 0o755)
|
||||
os.chmod(apply_executable, 0o755)
|
||||
mlz_config = os.path.join(os.getcwd(), "config_output", "mlz_tf_var_front")
|
||||
global_config = os.path.join(os.getcwd(), "config_output", "globals.tfvars.json")
|
||||
saca = os.path.join(os.getcwd(), "config_output", "saca-hub.tfvars.json")
|
||||
tier0 = os.path.join(os.getcwd(), "config_output", "tier-0.tfvars.json")
|
||||
tier1 = os.path.join(os.getcwd(), "config_output", "tier-1.tfvars.json")
|
||||
tier2 = os.path.join(os.getcwd(), "config_output", "tier-2.tfvars.json")
|
||||
|
||||
if keyVaultName:
|
||||
sp_id = secret_client.get_secret("login-app-clientid").value
|
||||
sp_pwd = secret_client.get_secret("login-app-clientid").value
|
||||
else:
|
||||
sp_id = os.getenv("MLZCLIENTID", "NotSet")
|
||||
sp_pwd = os.getenv("MLZCLIENTSECRET", "NotSet")
|
||||
|
||||
config_str = "{} {} {}".format(config_executable, mlz_config, "bypass")
|
||||
exec_str = "{} {} {} {} {} {} {} y {} {}".format(
|
||||
apply_executable, mlz_config, global_config, saca, tier0, tier1, tier2, sp_id, sp_pwd)
|
||||
|
||||
with open(exec_output, "w+") as out:
|
||||
creation = await asyncio.create_subprocess_exec(*config_str.split(), stderr=out, stdout=out)
|
||||
# This capture is setting to a dead object. If we want to do work with the process in the future
|
||||
# we have to do it here.
|
||||
await creation.wait()
|
||||
_ = await asyncio.create_subprocess_exec(*exec_str.split(), stderr=out, stdout=out)
|
||||
|
||||
return JSONResponse(content={"status": "success"}, status_code=200)
|
||||
|
||||
|
||||
|
||||
|
||||
# Execute a poll for the contents of a specific job, logs from terraform execution will be stored as text with
|
||||
# a job key ast he file name?
|
||||
@app.get("/poll")
|
||||
async def poll_results():
|
||||
"""
|
||||
Pol results is an async definition used by the /poll API query to return the results of the exec file
|
||||
|
||||
:return: will return the current contents of the results text file
|
||||
"""
|
||||
try:
|
||||
with open(exec_output, "r") as res:
|
||||
return JSONResponse(res.read(), status_code=200)
|
||||
except:
|
||||
return JSONResponse({"results": "No content for that job_num"}, status_code=200)
|
||||
|
||||
|
||||
# Primary entry for unvicorn
|
||||
# TODO: Replace with docker FlaskAPI Base image later
|
||||
if __name__ == "__main__":
|
||||
port = 80
|
||||
if len(sys.argv) > 1:
|
||||
port = int(sys.argv[1])
|
||||
uvicorn.run(app, host='0.0.0.0', port=port, debug=True)
|
|
@ -0,0 +1,9 @@
|
|||
uvicorn~=0.12.3
|
||||
dominate~=2.6.0
|
||||
fastapi~=0.62.0
|
||||
python-multipart
|
||||
aiofiles
|
||||
azure-keyvault
|
||||
msal~=1.8.0
|
||||
starlette~=0.13.6
|
||||
azure-identity
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
|
@ -0,0 +1,326 @@
|
|||
/*!
|
||||
* Bootstrap Reboot v4.5.3 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2020 The Bootstrap Authors
|
||||
* Copyright 2011-2020 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
||||
* Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)
|
||||
*/
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html {
|
||||
font-family: sans-serif;
|
||||
line-height: 1.15;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
article, aside, figcaption, figure, footer, header, hgroup, main, nav, section {
|
||||
display: block;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
||||
font-size: 1rem;
|
||||
font-weight: 400;
|
||||
line-height: 1.5;
|
||||
color: #212529;
|
||||
text-align: left;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
[tabindex="-1"]:focus:not(:focus-visible) {
|
||||
outline: 0 !important;
|
||||
}
|
||||
|
||||
hr {
|
||||
box-sizing: content-box;
|
||||
height: 0;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
abbr[title],
|
||||
abbr[data-original-title] {
|
||||
text-decoration: underline;
|
||||
-webkit-text-decoration: underline dotted;
|
||||
text-decoration: underline dotted;
|
||||
cursor: help;
|
||||
border-bottom: 0;
|
||||
-webkit-text-decoration-skip-ink: none;
|
||||
text-decoration-skip-ink: none;
|
||||
}
|
||||
|
||||
address {
|
||||
margin-bottom: 1rem;
|
||||
font-style: normal;
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
ol,
|
||||
ul,
|
||||
dl {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
ol ol,
|
||||
ul ul,
|
||||
ol ul,
|
||||
ul ol {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
dt {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
dd {
|
||||
margin-bottom: .5rem;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
margin: 0 0 1rem;
|
||||
}
|
||||
|
||||
b,
|
||||
strong {
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
small {
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
sub,
|
||||
sup {
|
||||
position: relative;
|
||||
font-size: 75%;
|
||||
line-height: 0;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
sub {
|
||||
bottom: -.25em;
|
||||
}
|
||||
|
||||
sup {
|
||||
top: -.5em;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #007bff;
|
||||
text-decoration: none;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: #0056b3;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
a:not([href]):not([class]) {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:not([href]):not([class]):hover {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
pre,
|
||||
code,
|
||||
kbd,
|
||||
samp {
|
||||
font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
pre {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
overflow: auto;
|
||||
-ms-overflow-style: scrollbar;
|
||||
}
|
||||
|
||||
figure {
|
||||
margin: 0 0 1rem;
|
||||
}
|
||||
|
||||
img {
|
||||
vertical-align: middle;
|
||||
border-style: none;
|
||||
}
|
||||
|
||||
svg {
|
||||
overflow: hidden;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
caption {
|
||||
padding-top: 0.75rem;
|
||||
padding-bottom: 0.75rem;
|
||||
color: #6c757d;
|
||||
text-align: left;
|
||||
caption-side: bottom;
|
||||
}
|
||||
|
||||
th {
|
||||
text-align: inherit;
|
||||
text-align: -webkit-match-parent;
|
||||
}
|
||||
|
||||
label {
|
||||
display: inline-block;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
button {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
button:focus {
|
||||
outline: 1px dotted;
|
||||
outline: 5px auto -webkit-focus-ring-color;
|
||||
}
|
||||
|
||||
input,
|
||||
button,
|
||||
select,
|
||||
optgroup,
|
||||
textarea {
|
||||
margin: 0;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
button,
|
||||
input {
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
button,
|
||||
select {
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
[role="button"] {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
select {
|
||||
word-wrap: normal;
|
||||
}
|
||||
|
||||
button,
|
||||
[type="button"],
|
||||
[type="reset"],
|
||||
[type="submit"] {
|
||||
-webkit-appearance: button;
|
||||
}
|
||||
|
||||
button:not(:disabled),
|
||||
[type="button"]:not(:disabled),
|
||||
[type="reset"]:not(:disabled),
|
||||
[type="submit"]:not(:disabled) {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
button::-moz-focus-inner,
|
||||
[type="button"]::-moz-focus-inner,
|
||||
[type="reset"]::-moz-focus-inner,
|
||||
[type="submit"]::-moz-focus-inner {
|
||||
padding: 0;
|
||||
border-style: none;
|
||||
}
|
||||
|
||||
input[type="radio"],
|
||||
input[type="checkbox"] {
|
||||
box-sizing: border-box;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
textarea {
|
||||
overflow: auto;
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
fieldset {
|
||||
min-width: 0;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
legend {
|
||||
display: block;
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
padding: 0;
|
||||
margin-bottom: .5rem;
|
||||
font-size: 1.5rem;
|
||||
line-height: inherit;
|
||||
color: inherit;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
progress {
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
[type="number"]::-webkit-inner-spin-button,
|
||||
[type="number"]::-webkit-outer-spin-button {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
[type="search"] {
|
||||
outline-offset: -2px;
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
[type="search"]::-webkit-search-decoration {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
::-webkit-file-upload-button {
|
||||
font: inherit;
|
||||
-webkit-appearance: button;
|
||||
}
|
||||
|
||||
output {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
summary {
|
||||
display: list-item;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
template {
|
||||
display: none;
|
||||
}
|
||||
|
||||
[hidden] {
|
||||
display: none !important;
|
||||
}
|
||||
/*# sourceMappingURL=bootstrap-reboot.css.map */
|
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
|
@ -0,0 +1,8 @@
|
|||
/*!
|
||||
* Bootstrap Reboot v4.5.3 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2020 The Bootstrap Authors
|
||||
* Copyright 2011-2020 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
||||
* Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)
|
||||
*/*,::after,::before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}article,aside,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:left;background-color:#fff}[tabindex="-1"]:focus:not(:focus-visible){outline:0!important}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#007bff;text-decoration:none;background-color:transparent}a:hover{color:#0056b3;text-decoration:underline}a:not([href]):not([class]){color:inherit;text-decoration:none}a:not([href]):not([class]):hover{color:inherit;text-decoration:none}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto;-ms-overflow-style:scrollbar}figure{margin:0 0 1rem}img{vertical-align:middle;border-style:none}svg{overflow:hidden;vertical-align:middle}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#6c757d;text-align:left;caption-side:bottom}th{text-align:inherit;text-align:-webkit-match-parent}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}[role=button]{cursor:pointer}select{word-wrap:normal}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none!important}
|
||||
/*# sourceMappingURL=bootstrap-reboot.min.css.map */
|
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
|
@ -0,0 +1,32 @@
|
|||
.custom-pane {
|
||||
background: #252f2f;
|
||||
border: 1px solid #000000;
|
||||
border-radius: 15px;
|
||||
}
|
||||
.form-elements {
|
||||
border-bottom: 2px solid #000000;
|
||||
}
|
||||
.page-header {
|
||||
margin-top: 80px;
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
.modal-dialog {
|
||||
width: 800px;
|
||||
}
|
||||
#terminal {
|
||||
font-family: Terminus,Consolas,Profont,"Andale Mono",Monaco,Inconsolata,Inconsolata-g,
|
||||
Unifont,Lime,"ClearlyU PUA",Clean,"DejaVu Sans Mono","Lucida Console",
|
||||
"Bitstream Vera Sans Mono",Freemono,"Liberation Mono",Dina,Anka,Droid Sans Mono,
|
||||
Anonymous Pro,Proggy fonts,Envy Code R,Gamow,Courier,"Courier New",Terminal,monospace;
|
||||
position: relative;
|
||||
font-size: 16px;
|
||||
padding: 10px;
|
||||
border-style: solid;
|
||||
border-width:2px;
|
||||
border-color: #414141;
|
||||
background-color: #000000;
|
||||
width: 765px;
|
||||
height: 500px;
|
||||
overflow: auto;
|
||||
color: #66FF66;
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
/* EzDeploy Javascript Cookie Management */
|
||||
|
||||
function createCookie(name,value,hours) {
|
||||
if (days) {
|
||||
var date = new Date();
|
||||
date.setTime(date.getTime()+(hours*60*1000));
|
||||
var expires = "; expires="+date.toGMTString();
|
||||
}
|
||||
else var expires = "";
|
||||
document.cookie = name+"="+value+expires+"; path=/";
|
||||
}
|
||||
|
||||
function readCookie(name) {
|
||||
var ca = document.cookie.split(';');
|
||||
for(var i=0;i < ca.length;i++) {
|
||||
var c = ca[i];
|
||||
while (c.charAt(0)==' ') c = c.substring(1,c.length);
|
||||
if (c.indexOf(name+"=") == 0) return c.substring(name.length+1,c.length);
|
||||
}
|
||||
return null;
|
||||
}
|
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
|
@ -17,19 +17,19 @@ PGM=$(basename "${0}")
|
|||
|
||||
if [[ "$#" -lt 2 ]]; then
|
||||
echo "apply_terraform.sh: initializes Terraform for a given directory using given a .env file for backend configuration"
|
||||
echo "usage: apply_terraform.sh <global variables file> <terraform configuration directory> <auto approve (y/n)>"
|
||||
echo "usage: apply_terraform.sh <global variables file> <terraform configuration directory> <var_file> <auto approve (y/n)>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
globalvars=$(realpath "${1}")
|
||||
|
||||
tf_dir=$(realpath "${2}")
|
||||
tf_name=$(basename "${tf_dir}")
|
||||
tf_name=$(basename "${3}")
|
||||
|
||||
config_vars="${tf_dir}/config.vars"
|
||||
tfvars="${tf_dir}/${tf_name}.tfvars"
|
||||
tfvars="${tf_dir}/${tf_name}"
|
||||
|
||||
auto_approve=${3:-n}
|
||||
auto_approve=${4:-n}
|
||||
|
||||
# check for dependencies
|
||||
. "${BASH_SOURCE%/*}/util/checkforazcli.sh"
|
||||
|
@ -38,14 +38,6 @@ auto_approve=${3:-n}
|
|||
# Validate necessary Azure resources exist
|
||||
. "${BASH_SOURCE%/*}/config/config_validate.sh" "${tf_dir}"
|
||||
|
||||
# Get the .tfvars file matching the terraform directory name
|
||||
if [[ ! -f "${tfvars}" ]]
|
||||
then
|
||||
echo "${PGM}: Could not find a terraform variables file with the name '${tfvars}' at ${tf_dir}"
|
||||
echo "${PGM}: Exiting."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Validate configuration file exists
|
||||
. "${BASH_SOURCE%/*}/util/checkforfile.sh" \
|
||||
"${config_vars}" \
|
||||
|
|
|
@ -26,12 +26,15 @@ if [[ "$#" -lt 1 ]]; then
|
|||
exit 1
|
||||
fi
|
||||
|
||||
mlz_config=$(realpath "${1}")
|
||||
tf_sub_id_raw=${2:-notset}
|
||||
tf_name_raw=${3:-notset}
|
||||
# Front End By Pass Check
|
||||
if [[ ${1} != "bypass" ]]; then
|
||||
mlz_config=$(realpath "${1}")
|
||||
tf_sub_id_raw=${2:-notset}
|
||||
tf_name_raw=${3:-notset}
|
||||
|
||||
# source variables from MLZ config
|
||||
. "${mlz_config}"
|
||||
# source variables from MLZ config
|
||||
. "${mlz_config}"
|
||||
fi
|
||||
|
||||
# remove hyphens for resource naming restrictions
|
||||
# in the future, do more cleansing
|
||||
|
@ -46,14 +49,26 @@ mlz_prefix="mlz-tf"
|
|||
mlz_sp_name_full="sp-${mlz_prefix}-${mlz_env_name_clean}"
|
||||
mlz_sa_name_full="mlztfsa${mlz_env_name_clean}${mlz_sub_id_clean}"
|
||||
mlz_kv_name_full="mlzkv${mlz_env_name_clean}${mlz_sub_id_clean}"
|
||||
mlz_acr_name_full="mlzacr${mlz_env_name_clean}${mlz_sub_id_clean}"
|
||||
mlz_fe_app_name_full="mlzfeapp${mlz_env_name_clean}${mlz_sub_id_clean}"
|
||||
mlz_instance_name_full="mlzfeinstance${mlz_env_name_clean}${mlz_sub_id_clean}"
|
||||
mlz_dns_name_full="mlzdep${mlz_env_name_clean}${mlz_sub_id_clean}"
|
||||
|
||||
# Name MLZ config resources
|
||||
export mlz_rg_name="rg-${mlz_prefix}-${mlz_env_name_clean}"
|
||||
export mlz_sp_name="${mlz_sp_name_full}"
|
||||
export mlz_sp_kv_name="${mlz_sp_name_full}-clientid"
|
||||
export mlz_sp_kv_password="${mlz_sp_name_full}-pwd"
|
||||
export mlz_sp_kv_name="serviceprincipal-clientid"
|
||||
export mlz_sp_kv_password="serviceprincipal-pwd"
|
||||
export mlz_login_app_kv_name="login-app-clientid"
|
||||
export mlz_login_app_kv_password="login-app-pwd"
|
||||
export mlz_sa_name="${mlz_sa_name_full:0:24}" # take the 24 characters of the storage account name
|
||||
export mlz_kv_name="${mlz_kv_name_full:0:24}" # take the 24 characters of the key vault name
|
||||
export mlz_acr_name="${mlz_acr_name_full:0:24}"
|
||||
export mlz_fe_app_name="${mlz_fe_app_name_full:0:24}"
|
||||
export mlz_instance_name="${mlz_instance_name_full:0:24}"
|
||||
export mlz_dns_name="${mlz_dns_name_full:0:24}"
|
||||
|
||||
# FE Resources
|
||||
|
||||
if [[ $tf_name_raw != "notset" ]]; then
|
||||
# remove hyphens for resource naming restrictions
|
||||
|
|
|
@ -25,23 +25,29 @@ if [[ "$#" -lt 1 ]]; then
|
|||
exit 1
|
||||
fi
|
||||
|
||||
mlz_tf_cfg=$(realpath "${1}")
|
||||
# Front End By Pass Check
|
||||
if [[ ${1} != "bypass" ]]; then
|
||||
|
||||
# Source variables
|
||||
. "${mlz_tf_cfg}"
|
||||
mlz_tf_cfg=$(realpath "${1}")
|
||||
|
||||
# Source variables
|
||||
. "${mlz_tf_cfg}"
|
||||
|
||||
# Create array of unique subscription IDs. The 'sed' command below search thru the source
|
||||
# variables file looking for all lines that do not have a '#' in the line. If a line with
|
||||
# a '#' is found, the '#' and ever character after it in the line is ignored. The output
|
||||
# of what remains from the sed command is then piped to grep to find the words that match
|
||||
# the pattern. These words are what make up the 'mlz_subs' array.
|
||||
mlz_sub_pattern="mlz_.*._subid"
|
||||
mlz_subs=$(< "${mlz_tf_cfg}" sed 's:#.*$::g' | grep -w "${mlz_sub_pattern}")
|
||||
subs=()
|
||||
else
|
||||
mlz_tf_cfg="bypass"
|
||||
fi
|
||||
|
||||
# generate MLZ configuration names
|
||||
. "${BASH_SOURCE%/*}/generate_names.sh" "${mlz_tf_cfg}"
|
||||
|
||||
# Create array of unique subscription IDs. The 'sed' command below search thru the source
|
||||
# variables file looking for all lines that do not have a '#' in the line. If a line with
|
||||
# a '#' is found, the '#' and ever character after it in the line is ignored. The output
|
||||
# of what remains from the sed command is then piped to grep to find the words that match
|
||||
# the pattern. These words are what make up the 'mlz_subs' array.
|
||||
mlz_sub_pattern="mlz_.*._subid"
|
||||
mlz_subs=$(< "${mlz_tf_cfg}" sed 's:#.*$::g' | grep -w "${mlz_sub_pattern}")
|
||||
subs=()
|
||||
|
||||
for mlz_sub in $mlz_subs
|
||||
do
|
||||
# Grab value of variable
|
||||
|
@ -52,6 +58,7 @@ do
|
|||
done
|
||||
|
||||
# Create Azure AD application registration and Service Principal
|
||||
# TODO: Lift the subscription scoping out of here and move into conditional
|
||||
echo "Verifying Service Principal is unique (${mlz_sp_name})"
|
||||
if [[ -z $(az ad sp list --filter "displayName eq '${mlz_sp_name}'" --query "[].displayName" -o tsv) ]];then
|
||||
echo "Service Principal does not exist...creating"
|
||||
|
@ -68,12 +75,15 @@ if [[ -z $(az ad sp list --filter "displayName eq '${mlz_sp_name}'" --query "[].
|
|||
--query appId \
|
||||
--output tsv)
|
||||
|
||||
# Get Service Principal ObjectId
|
||||
# Get Service Principal ObjectId
|
||||
sp_objid=$(az ad sp show \
|
||||
--id "http://${mlz_sp_name}" \
|
||||
--query objectId \
|
||||
--output tsv)
|
||||
|
||||
# Make available to calling scripts
|
||||
export sp_objid=${sp_objid}
|
||||
|
||||
# Assign Contributor role to Service Principal
|
||||
for sub in "${subs[@]}"
|
||||
do
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
[
|
||||
{
|
||||
"x-ms-resourceAppName": "Microsoft Graph",
|
||||
"resourceAppId": "00000003-0000-0000-c000-000000000000",
|
||||
"resourceAccess": [
|
||||
{
|
||||
"id": "b340eb25-3456-403f-be2f-af7a0d370277",
|
||||
"type": "Scope",
|
||||
"x-ms-name": "User.ReadBasic.All"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
|
@ -17,19 +17,19 @@ PGM=$(basename "${0}")
|
|||
|
||||
if [[ "$#" -lt 2 ]]; then
|
||||
echo "destroy_terraform.sh: initializes Terraform for a given directory using given a .env file for backend configuration"
|
||||
echo "usage: destroy_terraform.sh <global variables file> <terraform configuration directory> <auto approve (y/n)>"
|
||||
echo "usage: destroy_terraform.sh <global variables file> <terraform configuration directory> <var_file> <auto approve (y/n)>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
globalvars=$(realpath "${1}")
|
||||
|
||||
tf_dir=$(realpath "${2}")
|
||||
tf_name=$(basename "${tf_dir}")
|
||||
tf_name=$(basename "${3}")
|
||||
|
||||
config_vars="${tf_dir}/config.vars"
|
||||
tfvars="${tf_dir}/${tf_name}.tfvars"
|
||||
tfvars="${tf_dir}/${tf_name}"
|
||||
|
||||
auto_approve=${3:-n}
|
||||
auto_approve=${4:-n}
|
||||
|
||||
# check for dependencies
|
||||
. "${BASH_SOURCE%/*}/util/checkforazcli.sh"
|
||||
|
@ -38,14 +38,6 @@ auto_approve=${3:-n}
|
|||
# Validate necessary Azure resources exist
|
||||
. "${BASH_SOURCE%/*}/config/config_validate.sh" "${tf_dir}"
|
||||
|
||||
# Get the .tfvars file matching the terraform directory name
|
||||
if [[ ! -f "${tfvars}" ]]
|
||||
then
|
||||
echo "${PGM}: Could not find a terraform variables file with the name '${tfvars}' at ${tf_dir}"
|
||||
echo "${PGM}: Exiting."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Validate configuration file exists
|
||||
. "${BASH_SOURCE%/*}/util/checkforfile.sh" \
|
||||
"${config_vars}" \
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
# Copyright (c) Microsoft Corporation.
|
||||
# Licensed under the MIT License.
|
||||
#
|
||||
# shellcheck disable=SC1090,SC1091
|
||||
# SC1090: Can't follow non-constant source. Use a directive to specify location.
|
||||
# SC1091: Not following. Shellcheck can't follow non-constant source.
|
||||
#
|
||||
# This script builds and tags the docker image
|
||||
|
||||
set -e
|
||||
|
||||
error_log() {
|
||||
echo "${1}" 1>&2;
|
||||
}
|
||||
|
||||
# Check for Docker CLI
|
||||
if ! command -v docker &> /dev/null; then
|
||||
echo "Docker could not be found. Docker is required to build docker images."
|
||||
echo "see https://docs.docker.com/engine/install/ubuntu for installation instructions."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
usage() {
|
||||
echo "ezdeploy_docker.sh: If using 'load' will load from a specified zip file, if using 'build' will build the docker image from the dockerfile."
|
||||
error_log "usage: ezdeploy_docker.sh <load|build> {{default=build}}"
|
||||
}
|
||||
|
||||
if [[ "$#" -lt 1 ]]; then
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "INFO: building docker container"
|
||||
if [[ "${1}" == "build" ]]; then
|
||||
docker build -t lzfront "${BASH_SOURCE%/*}/../"
|
||||
elif [[ "${1}" == "load" ]]; then
|
||||
#TODO: Change this to a file pointer
|
||||
unzip mlz.zip .
|
||||
docker load -i mlz.tar
|
||||
else
|
||||
echo "Unrecognized docker strategy detected. Must be build or load"
|
||||
fi
|
||||
|
|
@ -17,8 +17,8 @@ error_log() {
|
|||
}
|
||||
|
||||
usage() {
|
||||
echo "mlz_tf_setup.sh: configure a resource group that contains Terraform state and a secret store"
|
||||
error_log "usage: mlz_tf_setup.sh <mlz tf config vars>"
|
||||
echo "mlz_tf_setup.sh: configure a resource group that contains Terraform state and a secret store, the presence of bypass indicates skipping primary creation"
|
||||
error_log "usage: mlz_tf_setup.sh <mlz tf config vars> <bypass>"
|
||||
}
|
||||
|
||||
if [[ "$#" -lt 1 ]]; then
|
||||
|
@ -49,8 +49,9 @@ create_tf_config() {
|
|||
##################################################
|
||||
|
||||
# generate MLZ configuration resources
|
||||
|
||||
. "${BASH_SOURCE%/*}/config/mlz_config_create.sh" "${mlz_tf_cfg}" "${mlz_env_name}" "${mlz_config_location}"
|
||||
if [[ ${2} != "bypass" ]]; then
|
||||
. "${BASH_SOURCE%/*}/config/mlz_config_create.sh" "${mlz_tf_cfg}" "${mlz_env_name}" "${mlz_config_location}"
|
||||
fi
|
||||
|
||||
create_tf_config "${mlz_saca_subid}" "${core_path}/saca-hub"
|
||||
create_tf_config "${mlz_tier0_subid}" "${core_path}/tier-0"
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
# Copyright (c) Microsoft Corporation.
|
||||
# Licensed under the MIT License.
|
||||
#
|
||||
# shellcheck disable=SC1090,SC1091
|
||||
# SC1090: Can't follow non-constant source. Use a directive to specify location.
|
||||
# SC1091: Not following. Shellcheck can't follow non-constant source.
|
||||
#
|
||||
# This script dumps the docker image and compresses it for transport to a target network
|
||||
|
||||
# Run Docker Setup Script
|
||||
|
||||
docker build -t lzfront "${BASH_SOURCE%/*}/../"
|
||||
docker save -o mlz.tar lzfront:latest
|
||||
zip mlz.zip mlz.tar
|
||||
rm mlz.tar
|
|
@ -0,0 +1,206 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
# Copyright (c) Microsoft Corporation.
|
||||
# Licensed under the MIT License.
|
||||
#
|
||||
# shellcheck disable=SC1090,SC1091
|
||||
# SC1090: Can't follow non-constant source. Use a directive to specify location.
|
||||
# SC1091: Not following. Shellcheck can't follow non-constant source.
|
||||
# SC2154: "var is referenced but not assigned". These values come from an external file.
|
||||
#
|
||||
# This script deploys container registries, app registrations, and a container instance to run the MLZ front end
|
||||
|
||||
set -e
|
||||
|
||||
error_log() {
|
||||
echo "${1}" 1>&2;
|
||||
}
|
||||
|
||||
usage() {
|
||||
echo "setup_ezdeploy.sh: Setup the Front End for MLZ"
|
||||
error_log "usage: setup_ezdeploy.sh -d <local|build|load> -s <subscription_id> -t <tenant_id> -l <location> -e <tf_env_name {{default=public}}> -m <mlz_env_name {{default=mlzdeployment}}> -p <web_port {{default=80}}> -0 <saca_subscription_id> -1 <tier0_subscription_id> -2 <tier1_subscription_id> -3 <tier2_subscription_id>"
|
||||
}
|
||||
|
||||
if [[ "$#" -lt 8 ]]; then
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
export tf_environment=public
|
||||
export mlz_env_name=mlzdeployment
|
||||
export web_port=80
|
||||
subs=()
|
||||
|
||||
while getopts "d:s:t:l:e:m:p:0:1:2:3:4:" opts; do
|
||||
case "${opts}" in
|
||||
d) export docker_strategy=${OPTARG}
|
||||
;;
|
||||
s) export mlz_config_subid=${OPTARG}
|
||||
subs+=("${OPTARG}")
|
||||
;;
|
||||
t) export mlz_tenantid=${OPTARG}
|
||||
;;
|
||||
l) export mlz_config_location=${OPTARG}
|
||||
;;
|
||||
e) export tf_environment=${OPTARG}
|
||||
;;
|
||||
m) export mlz_env_name=${OPTARG}
|
||||
;;
|
||||
p) export web_port=${OPTARG}
|
||||
;;
|
||||
0) export mlz_saca_subid=${OPTARG}
|
||||
subs+=("${OPTARG}")
|
||||
;;
|
||||
1) export mlz_tier0_subid=${OPTARG}
|
||||
subs+=("${OPTARG}")
|
||||
;;
|
||||
2) export mlz_tier1_subid=${OPTARG}
|
||||
subs+=("${OPTARG}")
|
||||
;;
|
||||
3) export mlz_tier2_subid=${OPTARG}
|
||||
subs+=("${OPTARG}")
|
||||
;;
|
||||
?)
|
||||
echo "Invalid option: -${OPTARG}."
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# generate MLZ configuration names
|
||||
. "${BASH_SOURCE%/*}/config/generate_names.sh" "bypass"
|
||||
|
||||
# create the subscription resources
|
||||
. "${BASH_SOURCE%/*}/config/mlz_config_create.sh" "bypass"
|
||||
|
||||
for sub in "${subs[@]}"
|
||||
do
|
||||
echo "Setting Contributor role assignment for ${mlz_sp_name} on subscription ID: ${sub}"
|
||||
az role assignment create \
|
||||
--role Contributor \
|
||||
--assignee-object-id "${sp_objid}" \
|
||||
--scope "/subscriptions/${sub}" \
|
||||
--assignee-principal-type ServicePrincipal \
|
||||
--output none
|
||||
done
|
||||
|
||||
echo "INFO: Setting current az cli subscription to ${mlz_config_subid}"
|
||||
az account set --subscription "${mlz_config_subid}"
|
||||
|
||||
# Handle Deployment of Login Services
|
||||
|
||||
# Handle Remote Deploy to a Container Instance
|
||||
if [[ $docker_strategy != "local" ]]; then
|
||||
echo "Creating ACR"
|
||||
az acr create \
|
||||
--resource-group "${mlz_rg_name}" \
|
||||
--name "${mlz_acr_name}" \
|
||||
--sku Basic
|
||||
|
||||
echo "Waiting for registry completion and running post process to enable admin on ACR"
|
||||
sleep 60
|
||||
az acr update --name "${mlz_acr_name}" --admin-enabled true
|
||||
|
||||
. "${BASH_SOURCE%/*}/ezdeploy_docker.sh" "$docker_strategy"
|
||||
|
||||
docker tag lzfront:latest "${mlz_acr_name}.azurecr.io/lzfront:latest"
|
||||
|
||||
echo "INFO: Logging into Container Registry"
|
||||
az acr login --name "${mlz_acr_name}"
|
||||
|
||||
ACR_REGISTRY_ID=$(az acr show --name "${mlz_acr_name}" --query id --output tsv)
|
||||
az role assignment create --assignee "$(az keyvault secret show --name "${mlz_sp_kv_name}" --vault-name "${mlz_kv_name}" --query value --output tsv)" --scope $ACR_REGISTRY_ID --role acrpull
|
||||
|
||||
echo "INFO: pushing docker container"
|
||||
docker tag lzfront:latest "${mlz_acr_name}".azurecr.io/lzfront:latest
|
||||
docker push "${mlz_acr_name}".azurecr.io/lzfront:latest
|
||||
ACR_LOGIN_SERVER=$(az acr show --name "${mlz_acr_name}" --resource-group "${mlz_rg_name}" --query "loginServer" --output tsv)
|
||||
|
||||
echo "INFO: creating instance"
|
||||
fqdn=$(az container create \
|
||||
--resource-group "${mlz_rg_name}"\
|
||||
--name "${mlz_instance_name}" \
|
||||
--image "$ACR_LOGIN_SERVER"/lzfront:latest \
|
||||
--dns-name-label "${mlz_dns_name}" \
|
||||
--environment-variables KEYVAULT_ID="${mlz_kv_name}" TENANT_ID="${mlz_tenantid}" LOCATION="${mlz_config_location}" SUBSCRIPTION_ID="${mlz_config_subid}" TF_ENV="${tf_environment}" MLZ_ENV="${mlz_env_name}" \
|
||||
--registry-username "$(az keyvault secret show --name "${mlz_sp_kv_name}" --vault-name "${mlz_kv_name}" --query value --output tsv)" \
|
||||
--registry-password "$(az keyvault secret show --name "${mlz_sp_kv_password}" --vault-name "${mlz_kv_name}" --query value --output tsv)" \
|
||||
--ports 80 \
|
||||
--query ipAddress.fqdn \
|
||||
--assign-identity \
|
||||
--output tsv)
|
||||
|
||||
echo "INFO: Giving Instance the necessary permissions"
|
||||
az keyvault set-policy \
|
||||
-n "${mlz_kv_name}" \
|
||||
--key-permissions get list \
|
||||
--secret-permissions get list \
|
||||
--object-id "$(az container show --resource-group "${mlz_rg_name}" --name "${mlz_instance_name}" --query identity.principalId --output tsv)"
|
||||
else
|
||||
fqdn="localhost"
|
||||
fi
|
||||
|
||||
if [[ $web_port != 80 ]]; then
|
||||
fqdn+=":$web_port"
|
||||
fi
|
||||
|
||||
|
||||
# Generate the Login EndPoint for Security Purposes
|
||||
echo "Creating App Registration to facilitate login capabilities"
|
||||
client_id=$(az ad app create \
|
||||
--display-name "${mlz_fe_app_name}" \
|
||||
--reply-urls "http://$fqdn/redirect" \
|
||||
--required-resource-accesses "${BASH_SOURCE%/*}"/config/mlz_login_app_resources.json \
|
||||
--query appId \
|
||||
--output tsv)
|
||||
|
||||
client_password=$(az ad app credential reset \
|
||||
--id "$client_id" \
|
||||
--query password \
|
||||
--output tsv)
|
||||
|
||||
echo "Storing client id at ${mlz_login_app_kv_name}"
|
||||
az keyvault secret set \
|
||||
--name "${mlz_login_app_kv_name}" \
|
||||
--subscription "${mlz_config_subid}" \
|
||||
--vault-name "${mlz_kv_name}" \
|
||||
--value "$client_id" \
|
||||
--output none
|
||||
|
||||
echo "Storing client secret at ${mlz_login_app_kv_password}"
|
||||
az keyvault secret set \
|
||||
--name "${mlz_login_app_kv_password}" \
|
||||
--subscription "${mlz_config_subid}" \
|
||||
--vault-name "${mlz_kv_name}" \
|
||||
--value "$client_password" \
|
||||
--output none
|
||||
|
||||
echo "KeyVault updated with Login App Registration secret!"
|
||||
echo "All steps have been completed you will need the following to access the configuration utility:"
|
||||
if [[ $docker_strategy == "local" ]]; then
|
||||
echo "Your environment variables for local execution are:"
|
||||
echo "Copy-Paste:"
|
||||
echo "Bash:"
|
||||
echo "export CLIENT_ID=$client_id"
|
||||
echo "export CLIENT_SECRET=$client_password"
|
||||
echo "export TENANT_ID=$mlz_tenantid"
|
||||
echo "export LOCATION=$mlz_config_location"
|
||||
echo "export SUBSCRIPTION_ID=$mlz_config_subid"
|
||||
echo "export TF_ENV=$tf_environment"
|
||||
echo "export MLZ_ENV=$mlz_env_name"
|
||||
echo "export MLZCLIENTID=$(az keyvault secret show --name "${mlz_sp_kv_name}" --vault-name "${mlz_kv_name}" --query value --output tsv)"
|
||||
echo "export MLZCLIENTSECRET=$(az keyvault secret show --name "${mlz_sp_kv_password}" --vault-name "${mlz_kv_name}" --query value --output tsv)"
|
||||
echo "Powershell:"
|
||||
echo "\$env:CLIENT_ID='$client_id'"
|
||||
echo "\$env:CLIENT_SECRET='$client_password'"
|
||||
echo "\$env:TENANT_ID='$mlz_tenantid'"
|
||||
echo "\$env:LOCATION='$mlz_config_location'"
|
||||
echo "\$env:SUBSCRIPTION_ID='$mlz_config_subid'"
|
||||
echo "\$env:TF_ENV='$tf_environment'"
|
||||
echo "\$env:MLZ_ENV='$mlz_env_name'"
|
||||
echo "\$env:MLZCLIENTID='$(az keyvault secret show --name "${mlz_sp_kv_name}" --vault-name "${mlz_kv_name}" --query value --output tsv)'"
|
||||
echo "\$env:MLZCLIENTSECRET='$(az keyvault secret show --name "${mlz_sp_kv_password}" --vault-name "${mlz_kv_name}" --query value --output tsv)'"
|
||||
|
||||
else
|
||||
echo "You can access the front end at http://$fqdn"
|
||||
fi
|
Загрузка…
Ссылка в новой задаче