* fixed conflict merge

* ee

* bnm

* yh

* vv

* sd

* bn

* xx

* vb

* tt

* ss

* zz

* remove sub ids

* aa

* updates

* ff

* updates

* tt

* updates

* mm

* rr

* Added info Azure cli to remove legal hold & other misc updates

* Fix typos

* Moved env variables for toolkit & subscription in the code

* ss

* kk

* Adding Az.Accounts to dockerfile

* cc

* ii

* ll

* yy

* vv

* cc

* ee

* Added all azure regions to AzureBastion module

* nn

* gg

* tt

* dd

* Adding install module in the code itself

* jk

* Added condition to connect to azure & install modules for dev ops

* qaz

* wsx

* bb

* Commented env variables in debug

* ff

* HUB vnet module

* changed MSVDI to connect to shrd svcs hub

* dummy values for config files

* changed para for msvdi with shrd svcs

* do not need to lowercase regions so commented out

* added variables to file so don't need to input

* new prereq script. Not necessary to run

* readme for shared services

* updated readme

* Update

* edc

* Topological path for DevOps pipeline

* test

* Update

* Running individual modules

* Updates

* updated comments

* new modules

* Create dockflow.yml

* Updates to SharedServices & MS-VDI readme

* qq

* Added more info on password restrictions

* Update

* 56

* 985

* 12

* 67

* 45

* 12

* 678

* 12

* 456

* tt

* 12

* 12

* 1q23

* 125

* 343

* 25

* 345

* 2134

* 12

* 2

* 454

* 124

* 312

* 12

* 23

* 34

* mylife

* q3

* 12

* 24

* q1234

* 696

* qw23

* q12e4

* w5

* 213

* 2198

* qw

* 255

* 89876

* 447

* 3242

* 89

* 43234

* 2342342

* q4eq3214

* 87

* 323

* 2345

* 123456

* New version of code for github action

* updates to files

* updated av set infoo

* 789234

* 234143

* 24223412342

* Teardown test

* Copied workflow from Jack's branch

* new changes

* update to readme in shrdsvcs

* new document for github actions

* 234

* adding changes to script for cleanup

* update readme

* update readme

* sdf

* 235

* 123

* 2345

* new changes to readme

* new changes to readme

* readme

* readme

* readmeupdate

* readme

* red

* read

* readme

* 1234

* readme

* 7897894

* update readme shrd svcs

* 345

* new changes to readme

* removed the cleanup and added to different script

* new change to clean up script

* Updates to shared services readme

* update

* 234

* Added passing parameters for subscription & tenant to parameters.json for shared services

* update for networkwatcher

* removed statement in av sets

* Test GH Actions

* Test GH Actions

* Update

* Update

* Cleared values

* Update

* changes to dockerfile version.

* Update

* Update readme

* Update README.md

* Updates to docs - added SPN info

* All documentation updates - removed personal GH repo reference & referencing shared services deployment in quickstart

* Added release notes

* Update

* no change

* added password randomization

* no change

* added sentinel changes

* formatting

* sentinel change and secret changes to kv

* secret changes to kv

* sentinel changes

* dublicate code correction... No code change

* added sentinel env var

* Test Gov Deployment

* updated SS readme

* naming convention changes

* example of inputfile for master script

* updated readme

* updated docker yml fiile

* master orchestration script

* new github actions doc

* master orchestration documentation

* new env document

* updated MS-VDI parameters with ctx

* updated with ps7 requirements

* new windows virtual desktop environment

* added new artifact location parameter

* added spoke env

* doc

* new Vnet peering module

* application group module

* host pool module

* added output for script

* doc edit

* new images for docs

* updated doc

* disable resources

* docedit

* doc edit

* shared services as a spoke

* doc edit

* docedit

* doc updated

* updated for vms

Co-authored-by: RKSelvi <42325057+RKSelvi@users.noreply.github.com>
Co-authored-by: Selvi Kalaiselvi <selvi.kalaiselvi@appliedis.com>
Co-authored-by: Steve Downs <33630027+SteveDatAzureGov@users.noreply.github.com>
This commit is contained in:
jvalley19 2020-06-17 13:19:50 -04:00 коммит произвёл GitHub
Родитель 352150b580
Коммит 3ba087d6a1
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
72 изменённых файлов: 7022 добавлений и 62 удалений

6
.github/workflows/README.md поставляемый
Просмотреть файл

@ -46,10 +46,10 @@
3. #### In your dockerimage.yml file you will need to change the following values that suit your need
- ORGANIZATION_NAME
- AZURE_LOCATION
- Update (optional) "uses: Azure" to your GitHub repo name.
- uses: **Azure**/vdc@master
- Update "uses" to your GitHub repo name.
- uses: [YOUR_GITHUB_NAME]/vdc@master
- Please keep the AZURE_DISCOVERY_URL as is
4. #### Once you have all these changes and updated your GitHub secrets you can push the changes to your repository.

4
.github/workflows/dockerimage.yml поставляемый
Просмотреть файл

@ -17,7 +17,7 @@ jobs:
ADMIN_USER_PWD: ${{ secrets.ADMIN_USER_PWD }}
DOMAIN_ADMIN_USERNAME: ${{ secrets.DOMAIN_ADMIN_USERNAME }}
DOMAIN_ADMIN_USER_PWD: "Random"
ORGANIZATION_NAME : "jvgovm"
ORGANIZATION_NAME : "msgovjv"
AZURE_LOCATION : "USGov Virginia"
AZURE_ENVIRONMENT_NAME : "AzureUSGovernment"
TENANT_ID : ${{ secrets.TENANT_ID }}
@ -34,4 +34,4 @@ jobs:
- name: Build the Docker image & Deploy
id : hello
uses: Azure/vdc@master
uses: "[REPOSITORY_NAME]"/vdc@master

Просмотреть файл

@ -0,0 +1,176 @@
# VDC Master Script
## ONLY EDIT THE BELOW LINE (LINE 4)##
$inputFile = (Get-Content -Path C:\inputFile.json) | ConvertFrom-Json
# Set the variables that will not change through the deployments of the VDC toolkit
$env:numShrdSvcs = $inputFile.sharedservices.Count
$env:numMSVDI = $inputFile.MSVDI.Count
$ENV:AZURE_DISCOVERY_URL = $inputFile.azureDiscoveryURL
$ENV:AZURE_SENTINEL = $inputFile.azureSentinel
$ENV:ORGANIZATION_NAME = $inputFile.organizationName
$ENV:AZURE_ENVIRONMENT_NAME = $inputFile.azureEnvironmentName
$ENV:TENANT_ID = $inputFile.tenantID
$ENV:HUB_SUB_ID = $inputFile.SharedServices.hub1.subscriptionID
#Location for artifact storage account
$ENV:ARTIFACT_LOCATION = $inputFile.SharedServices.hub1.location
Write-Host "Welcome to the VDC toolkit deployment. Starting the deployment for $ENV:ORGANIZATION_NAME organization. " -ForegroundColor Green
Write-Host `n"You choose to deploy $ENV:numShrdSvcs Shared Service environments." -ForegroundColor Cyan
Write-Host `n"You choose to deploy $ENV:numMSVDI MS-VDI environments." -ForegroundColor Cyan
Function Get-newEnvVariablesSS {
# The below arrays will hold all the env variables for each MS-VDI deployment
$adminUserPWD=@()
$domainAdminUsername=@()
$domainAdminPWD=@()
# The loop below will determine all the env variables for each deployment
For ($i=0; $i -lt $env:numShrdSvcs; $i++) {
$int = $i+1
$hub = "hub" + $int
Write-Host `n"Enter the following secrets for $hub :" -Foregroundcolor Cyan
$ADMIN_USER_PWD = Read-Host -Prompt "What is the VM Admin Password for the $hub deployment? `nEnter 'Random' for a random password"
$adminUserPWD += $ADMIN_USER_PWD
$DOMAIN_ADMIN_USERNAME = Read-Host -Prompt "What is the Domain Account UserName for the $hub deployment?"
$domainAdminUsername += $DOMAIN_ADMIN_USERNAME
$DOMAIN_ADMIN_USER_PWD = Read-Host -Prompt "What is the Domain Account Admin Password for the $hub deployment? `nEnter 'Random' for a random password"
$domainAdminPWD += $DOMAIN_ADMIN_USER_PWD
}
return $adminUserPWD, $domainAdminUsername, $domainAdminPWD
}
Function Get-newEnvVariablesMSVDI {
# The below arrays will hold all the env variables for each MS-VDI deployment
$adminUserPWDMsVDI=@()
$domainAdminUsernameMsVDI=@()
$domainAdminPWDMsVDI=@()
# The loop below will determine all the env variables for each deployment
For ($i=0; $i -lt $env:numMSVDI; $i++) {
$int = $i+1
$msvdi = "MSVDI" + $int
Write-Host `n"Enter the following secrets for $msvdi :" -Foregroundcolor Cyan
$ADMIN_USER_PWD = Read-Host -Prompt "What is the VM Admin Password for the $msvdi deployment? `nEnter 'Random' for a random password"
$adminUserPWDMsVDI += $ADMIN_USER_PWD
$DOMAIN_ADMIN_USERNAME = Read-Host -Prompt "What is the Domain Account UserName for the $msvdi deployment?"
$domainAdminUsernameMsVDI += $DOMAIN_ADMIN_USERNAME
$DOMAIN_ADMIN_USER_PWD = Read-Host -Prompt "What is the Domain Account Admin Password for the $msvdi deployment? `nEnter 'Random' for a random password"
$domainAdminPWDMsVDI += $DOMAIN_ADMIN_USER_PWD
}
return $adminUserPWDMsVDI, $domainAdminUsernameMsVDI, $domainAdminPWDMsVDI
}
# Get env secrets for Shared Services
$adminUserPWD, $domainAdminUsername, $domainAdminPWD = Get-newEnvVariablesSS
# Get env secrets for MSVDI
$adminUserPWDMsVDI, $domainAdminUsernameMsVDI, $domainAdminPWDMsVDI = Get-newEnvVariablesMSVDI
For($i=0; $i -lt $env:numShrdSvcs; $i++) {
$int = $i+1
$hub = "hub$int"
Write-Host `n"Setting Environment Variables for $hub" -ForegroundColor Green
$ENV:AZURE_LOCATION = $inputFile.SharedServices.$hub.location
$ENV:SUBSCRIPTION_ID = $inputFile.SharedServices.$hub.subscriptionID
$ENV:KEYVAULT_MANAGEMENT_USER_ID = $inputFile.SharedServices.$hub.keyvaultobjectID
$ENV:DEVOPS_SERVICE_PRINCIPAL_USER_ID = $inputFile.SharedServices.$hub.devopsID
$ENV:ADMIN_USER_SSH = $inputFile.SharedServices.$hub.adminSSHPubKey
$ENV:FolderName = $inputFile.SharedServices.$hub.folderName
$ENV:ADMIN_USER_NAME = $inputFile.SharedServices.$hub.vmAdminUserName
$ENV:ADMIN_USER_PWD = $adminUserPWD[$i]
$ENV:DOMAIN_ADMIN_USER_PWD = $domainAdminPWD[$i]
$ENV:DOMAIN_ADMIN_USERNAME = $domainAdminUsername[$i]
Write-Host `n"Starting the deployment for $hub. Orchestration using directory folder: $ENV:FolderName"
Write-Host $ENV:ADMIN_USER_NAME
Write-Host $ENV:ADMIN_USER_PWD
Write-Host $ENV:AZURE_LOCATION
Write-Host $ENV:SUBSCRIPTION_ID
Write-Host $ENV:FolderName
Write-Host $ENV:AZURE_SENTINEL
Write-Host $ENV:AZURE_DISCOVERY_URL
Write-Host $ENV:ORGANIZATION_NAME
Write-Host $ENV:DOMAIN_ADMIN_USER_PWD
Write-Host $ENV:DOMAIN_ADMIN_USERNAME
Write-Host $ENV:KEYVAULT_MANAGEMENT_USER_ID
Write-Host $ENV:DEVOPS_SERVICE_PRINCIPAL_USER_ID
Write-Host $ENV:AZURE_ENVIRONMENT_NAME
Write-Host $ENV:ADMIN_USER_SSH
Write-Host $ENV:TENANT_ID
sleep -Seconds 5
./Orchestration/OrchestrationService/Pre_req_script.ps1
sleep -Seconds 5
./Orchestration/OrchestrationService/ModuleConfigurationDeployment.ps1 -DefinitionPath ./Environments/$env:folderName/definition.json
sleep -Seconds 5
}
For($i=0; $i -lt $env:numMSVDI; $i++) {
$int = $i+1
$MSVDI = "MSVDI$int"
Write-Host `n"Setting Environment Variables for $MSVDI" -ForegroundColor Green
$ENV:AZURE_LOCATION = $inputFile.MSVDI.$MSVDI.location
$ENV:SUBSCRIPTION_ID = $inputFile.MSVDI.$MSVDI.subscriptionID
$ENV:KEYVAULT_MANAGEMENT_USER_ID = $inputFile.MSVDI.$MSVDI.keyvaultobjectID
$ENV:DEVOPS_SERVICE_PRINCIPAL_USER_ID = $inputFile.MSVDI.$MSVDI.devopsID
$ENV:ADMIN_USER_SSH = $inputFile.MSVDI.$MSVDI.adminSSHPubKey
$ENV:FolderName = $inputFile.MSVDI.$MSVDI.folderName
$ENV:ADMIN_USER_NAME = $inputFile.MSVDI.$MSVDI.vmAdminUserName
$ENV:ADMIN_USER_PWD = $adminUserPWD[$i]
$ENV:DOMAIN_ADMIN_USER_PWD = $domainAdminPWD[$i]
$ENV:DOMAIN_ADMIN_USERNAME = $domainAdminUsername[$i]
Write-Host `n"Starting the deployment for $MSVDI. Orchestration using directory folder: $ENV:FolderName"
./Orchestration/OrchestrationService/Pre_req_script.ps1
sleep -Seconds 5
./Orchestration/OrchestrationService/ModuleConfigurationDeployment.ps1 -DefinitionPath ./Environments/$env:Foldername/definition.json
sleep -Seconds 5
Write-Host $ENV:ADMIN_USER_NAME
Write-Host $ENV:ADMIN_USER_PWD
Write-Host $ENV:AZURE_LOCATION
Write-Host $ENV:SUBSCRIPTION_ID
Write-Host $ENV:FolderName
Write-Host $ENV:AZURE_SENTINEL
Write-Host $ENV:AZURE_DISCOVERY_URL
Write-Host $ENV:ORGANIZATION_NAME
Write-Host $ENV:DOMAIN_ADMIN_USER_PWD
Write-Host $ENV:DOMAIN_ADMIN_USERNAME
Write-Host $ENV:KEYVAULT_MANAGEMENT_USER_ID
Write-Host $ENV:DEVOPS_SERVICE_PRINCIPAL_USER_ID
Write-Host $ENV:AZURE_ENVIRONMENT_NAME
Write-Host $ENV:ADMIN_USER_SSH
Write-Host $ENV:TENANT_ID
}

Просмотреть файл

@ -1,6 +1,6 @@
{
"Comments": "Cleaned up from deployment",
"TenantId": "00000-0000000-000000-0000-0",
"Comments": "ToolKit for creating a new Virtual Data Center",
"TenantId": "000000-000-0000-0000",
"SubscriptionId": "000000-000-0000-0000",
"Location": "DUMMYVALUE"
"Location": "USGov Arizona"
}

36
Docs/github_actions.md Normal file
Просмотреть файл

@ -0,0 +1,36 @@
Getting started with GitHub Actions and the VDC toolkit
1. Ensure you have the latest code when setting up your action pipeline
a. Files you need before proceeding with your actions
i. 'dockerfile' in your root repository
ii. 'action.yml' in your root repository
iii. 'entrypoint.ps1' in your root repository
iv. 'dockerimage.yml' under the "vdc/.github/workflows" directory
2. You will also need to setup your github secrets for the pipeline to use
a. You will need the following secrets
i. SERVICE_PRINCIPAL
ii. SERVICE_PRINCIPAL_PASS
iii. DEVOPS_SERVICE_PRINCIPAL_USER_ID
iv. ADMIN_USER_NAME
v. ADMIN_USER_PWD
vi. DOMAIN_ADMIN_USERNAME
vii. DOMAIN_ADMIN_USER_PWD
viii. TENANT_ID
ix. SUBSCRIPTION_ID
x. KEYVAULT_MANAGEMENT_USER_ID
xi. ADMIN_USER_SSH
b. To add these secrets in your Github repository navigate to
i. "Settings" -> "Secrets"
1) Then add each secret with exactly the name above
- *You do not need "" around your secret values.
3. In your dockerimage.yml file you will need to change the following values that suit your need
a. ORGANIZATION_NAME
b. AZURE_LOCATION
c. Please keep the AZURE_DISCOVERY_URL as is
4. Once you have all these changes and updated your Github secrets you can push the changes to your repository. Upon the "push" you will kick off an action which will deploy the shared services and ms-vdi resources.

Просмотреть файл

@ -0,0 +1,22 @@
## Environment Folder Replication
1. Copy the SharedServices or MS-VDI folders however many times you want to deploy those environments
- Within the MS-VDI folders you will need to change the following lines per folder copy or per spoke environment
- **Parameters.json** for ms-vdi
1. Make sure the folder name is unique
1. Ex: "MS-VDI-SPOKE-2"
2. Change line 2: Deployment name (unique)
3. Change line 29: VNET spoke address prefix
4. Change line 47: VNET spoke address prefix
2. If you are going to be deploying multiple "Shared Services" environments you will need to utilize:
a. [Shared Services SPOKE](../../Environments/SharedServices-SPOKE) folder in the environment directory
b. This folder has the necessary changes for deploying multiple iterations of shared services.
c. Copy this folder however many times you need to deploy shared services.
**For any resources you do not want to deploy you must add the following to the Orchestration.JSON file**
- "Enabled": false,
This will ensure the resource is not deployed for the environment. This applies to any folder under "environments"
![](/images/orchestration_enable.png)

Просмотреть файл

@ -0,0 +1,52 @@
## Setting up the inputFile.JSON for the master orchestration script
The inputFile.json should be placed in an accessible folder so that the MasterOrchestration script can retrieve it for manipulation.
An example of the input file: [Input File Example](../../inputFile.json)
The deployment admin will need to set the following values (example below in Picture 1):
1. Tenant ID
2. azureEnvironmentName
3. organizationName
4. azureDiscoveryURL
5. azureSentinel
![Picture 1](/images/input_file_ex.png)
*Picture 1*
**NOTE: DO NOT CHANGE "SharedServices" (In the example picture 2 below its line 8)**
**NOTE: DO NOT CHANGE the name iterations of "Hub1" (In the example picture 2 example below its line 10, 22)**
- The objects under "SharedServices" represent the shared services deployments
- NOTE: The first object ("Hub1") under shared services will act as the hub for the VDC toolkit. All other environments will be peered with this hub for a true hub and spoke topology.
- In the example below there are two shared service deployments.
- Hub1 will be the hub for the VDC toolkit
- Hub2 will be a shared services spoke for the VDC toolkit
- In this case the Deployment admin will have to manipulate the shared services folders
- There is no limit to the number of shared service environments you can deploy
- IF your organization only needs 1 Shared Services environment delete the second "hub2"
The deployment admin should change the following values under each Shared Service
1. SubscriptionID
2. Location
3. keyVaultObjectID
4. devOpsID
5. adminSSHPubKey
6. vmAdminUserName
7. folderName
1. This is the copied folder of the shared services
![Picture 2](/images/input_file_ex2.png)
*Picture 2*
After the shared services configuration is complete the deployment admin should change the MSVDI variables
DO NOT CHANGE Line 34 in the picture 3 below. This value "MSVDI" represents the msvdi deployments
NOTE: if you wish to add more spokes you must use the value iteration below
- "MSVDI1" Line 36
- "MSVDI2" Line 48
- And so on "MSVDI3"
![Picture 3](/images/input_file_ex3.png)
*Picture 3*

Просмотреть файл

@ -0,0 +1,35 @@
## Master Orchestration Script
The master orchestration script is located under the "Config" directory in the VDC toolkit repository.
[Master Orchestration Script](../../Config/masterOrchestration.ps1)
This will be the script the deployment admin executes to run the full VDC orchestration
The deployment admin should only change one line in the script. In the picture below its line 3.
- This is the location of the input file.
![](/images/inputFile_line_change.png)
*Picture 1*
Once you have your input file complete and the environments you wish to deploy folders copied and configured. You can call the master script for the deployment.
In PowerShell 7 command prompt - navigate to the directory where the vdc toolkit is located. In the root directory
- First you must login to Azure with an account that can access all the subscriptions you wish to deploy too
- Next you must call the masterOrchestration.ps1 file
- **./config/masterOrchestration.ps1**
- Make sure the information is correct for the number of shared services and msvdi environments you wish to deploy
- These numbers should align with the number of arrays under each object in the inputFile.json
![](/images/master_script_ex.png)
*Picture 2*
- Next you will be prompted to enter the password information for each deployment
- VM Admin password
- Domain Admin UserName
- Domain Admin Password
- If you want random passwords for these variables please enter "random" for the prompt
Once you fill out all the secret information the first deployment will kick off.
Deployment will go in the order below:
1. Hub1 - this is the master hub which everything is peered too
2. Hub2-X - These are optional spoke shared service environments
3. MSVDI1-X - These are spoke MSVDI environments. These are also optional

Просмотреть файл

@ -0,0 +1,28 @@
## Master Orchestration Overview
#### The master orchestration script is used to deploy multiple environments by executing only one script.
Two environment types exist with v1 of the master orchestration script
- "Shared Services" - located [SharedServices](../../Environments/SharedServices)
- "MS-VDI" - located [MS-VDI](../../Environments/MS-VDI)
The topology for the VDC toolkit is a hub and spoke model. The "shared services" environment(s) act as the hub and the "ms-vdi" environment(s) act as the
spokes.
The "Shared Services" environment can be broken down into multiple "shared service" environments if necessary. For example, if an organization wanted to split up the
Active Directory components into a separate VNET and peer to the hub that can be done with the master orchestration script.
Note: For testing purposes we suggest using one hub to begin and multiple spokes
The "MS-VDI" environment can be replicated 'X' number of times for the orchestration. Each spoke MS-VDI environment will be peered to the "Shared Services" or HUB environment.
Refer to the [folder_Replication](../masterOrchestration/folder_replication.md) for more information on how to create multiple spoke "MS-VDI" environments.
The master orchestration script has 3 prerequisites before the deployment admin can execute the script.
1. [Folder Replication](../masterOrchestration/folder_replication.md)
2. [Input File](../masterOrchestration/input_File.md)
3. [Master Orchestration Script](../masterOrchestration_script.md)
Once these 3 requirements are satified the deployment admin can execute the master orchestration script to deploy the full VDC environment.
#### TearDown Environment
Please refer to the [Tear Down Document](../masterOrchestration/tearDownEnvironment.md) when destroying an environment.
The teardown feature will remove individual environments.

Просмотреть файл

@ -0,0 +1,46 @@
## Tear Down Environment
When tearing down spokes or shared services environments you must make sure to complete the following pre-reqs before running the teardown script
1. You must be in the correct directory where you first deployed the environments
2. You will need to set your environment variables listed below
1. **$ENV:ORGANIZATION_NAME** = "jvgovern"
- This must be the value used when deployed initially.
2. **$ENV:TENANT_ID** = "35f102bf-a2d5-4531-86a3-fb1ba0d6725e"
- This must be the value used when deployed initially.
3. **$ENV:SUBSCRIPTION_ID** = "8780edd9-dcbd-47cd-8aef-6bc3820754a9"
- This must be the **Sub ID of the environment you wish to tear down**.
4. **$ENV:AZURE_ENVIRONMENT_NAME** = "AzureUSGovernment"
- This must be the environment name if you used Gov or Commercial
5. $ENV:AZURE_LOCATION = "USGov Virginia"
- Not sure yet
6. $ENV:KEYVAULT_MANAGEMENT_USER_ID = "cd21365a-be74-4e64-92e1-9dd6cd872f38"
- This can be arbitrary
7. $ENV:DEVOPS_SERVICE_PRINCIPAL_USER_ID = "cd21365a-be74-4e64-92e1-9dd6cd872f38"
- This can be arbitrary
8. $ENV:AZURE_DISCOVERY_URL = "https://management.azure.com/metadata/endpoints?api-version=2019-05-01"
- This can be arbitrary
9. $ENV:DOMAIN_ADMIN_USERNAME = "Arb"
- This can be arbitrary
10. $ENV:DOMAIN_ADMIN_USER_PWD = "Arb"
- This can be arbitrary
11. $ENV:AZURE_SENTINEL = "true"
- This can be arbitrary
12. $ENV:ADMIN_USER_NAME= "Arb"
- This can be arbitrary
13. $ENV:ADMIN_USER_PWD = "Arb"
- This can be arbitrary
14. $ENV:ADMIN_USER_SSH = "Arb"
- This can be arbitrary
15. **$ENV:HUB_SUB_ID** = "888888888888888"
- This value needs to be the subscription ID for the HUB environment (master Shared Services)
- If you are tearing down spokes this value can be arbitrary
16. $ENV:ARTIFACT_LOCATION = 'Arb"
- This should be arbitrary unless you want to delete the Artifact storage account
- You should only delete this if you plan to tear down the entire vdc toolkit.
- However, if you mistakenly delete the artifact RG any new vdc build will create a new one.
3. Next you will need to sign in to Azure using the following command
- Connect-AzAccount -Tenant $env:TENANT_ID -SubscriptionId $env:SUBSCRIPTION_ID -EnvironmentName $env:AZURE_ENVIRONMENT_NAME
4. Run the pre-req script so that the proper config files are updated
- .\Orchestration\OrchestrationService\Pre_req_script.ps1
5. ./Orchestration/OrchestrationService/ModuleConfigurationDeployment.ps1 -TearDownEnvironment -DefinitionPath ./Environments/MS-VDI/definition.json

Просмотреть файл

@ -19,8 +19,7 @@ After these prerequisties are installed, we will [clone the reposistory on GitHu
This quickstart assumes that you will be interacting with the toolkit through the Docker image.
## Deploy Shared Services
**Follow the steps [from here](./environments/sharedservices/../../../Environments/SharedServices/readme.md) to deploy Shared Services environment**
Follow the steps [from here](./environments/sharedservices/../../../Environments/SharedServices/readme.md) to deploy Shared Services environment
### Next steps

Просмотреть файл

@ -29,49 +29,33 @@
}
},
{
"Name": "VirtualNetworkPeeringHub",
"ModuleDefinitionName": "VirtualNetworkPeering",
"ResourceGroupName": "${Parameters.ModuleConfigurationParameters.SharedServices.VirtualNetworkHUB.ResourceGroupName}",
"Name": "VirtualNetworkPeering",
"ModuleDefinitionName": "VirtualNetworkPeeringCross",
"ResourceGroupName": "${Parameters.ModuleConfigurationParameters.VirtualNetworkSPOKE.ResourceGroup}",
"DependsOn": [
"VirtualNetworkHUB",
"VirtualNetworkSPOKE"
],
"Deployment": {
"OverrideParameters": {
"localVnetName": {
"value": "${Parameters.ModuleConfigurationParameters.SharedServices.VirtualNetworkHUB.Name}"
},
"peeringName": {
"value": "${Parameters.ModuleConfigurationParameters.VirtualNetworkPeering.LocalPeering.Name}"
},
"remoteVirtualNetworkId": {
"value": "${Parameters.ModuleConfigurationParameters.SharedServices.VirtualNetworkSPOKE.Id}"
},
"useRemoteGateways": {
"value": false
}
}
}
},
{
"Name": "VirtualNetworkPeeringSPOKE",
"ModuleDefinitionName": "VirtualNetworkPeering",
"ResourceGroupName": "${Parameters.ModuleConfigurationParameters.VirtualNetworkSPOKE.ResourceGroup}",
"DependsOn": [
"VirtualNetworkSPOKE",
"VirtualNetworkHUB"
],
"Deployment": {
"OverrideParameters": {
"localVnetName": {
"value": "${Parameters.ModuleConfigurationParameters.VirtualNetworkSPOKE.Name}"
},
"peeringName": {
"value": "${Parameters.ModuleConfigurationParameters.VirtualNetworkPeering.LocalPeering.Name}"
"remoteVnetName": {
"value": "${Parameters.ModuleConfigurationParameters.SharedServices.VirtualNetworkHUB.Name}"
},
"remoteVirtualNetworkId": {
"value": "${Parameters.ModuleConfigurationParameters.SharedServices.VirtualNetworkHUB.Id}"
"localSubscriptionID": {
"value": "${Subscriptions.VDCVDI.SubscriptionId}"
},
"remoteSubscriptionID": {
"value": "${Subscriptions.SharedServices.SubscriptionId}"
},
"localResourceGroup": {
"value": "${Parameters.ModuleConfigurationParameters.VirtualNetworkSPOKE.ResourceGroup}"
},
"remoteResourceGroup": {
"value": "${Parameters.ModuleConfigurationParameters.SharedServices.VirtualNetworkHUB.ResourceGroupName}"
},
"useRemoteGateways": {
"value": false
}
@ -250,6 +234,19 @@
}
},
{
"Name": "UploadScriptsToArtifactsStorage",
"Comments": "Upload Scripts to Artifacts Storage",
"DependsOn": [
"ArtifactsStorageAccount"
],
"Script": {
"Command": "Write-Host 'Adding Scripts...'; Import-Module -Name Az.Storage; $ctx = New-AzStorageContext -StorageAccountName reference(ArtifactsStorageAccount.storageAccountName) -SasToken '?reference(ArtifactsStorageAccount.storageAccountSasToken)'; New-AzRmStorageContainer -Name scripts -ResourceGroupName reference(ArtifactsStorageAccount.storageAccountResourceGroup) -StorageAccountName reference(ArtifactsStorageAccount.storageAccountName); Get-ChildItem -LiteralPath '../../Scripts/Windows' -File -Recurse | % { Set-AzStorageBlobContent -Context $ctx -Container scripts -Force -File $($_.FullName) -Blob Windows/$($_.Name)}",
"Arguments" : {
}
}
},
{
"Name": "JumpboxASG",
"ModuleDefinitionName": "ApplicationSecurityGroups",
"ResourceGroupName": "${Parameters.ModuleConfigurationParameters.ApplicationSecurityGroups.ResourceGroup}",
@ -344,6 +341,338 @@
}
}
}
},
{
"Name": "citrixStoreFrontVMs",
"ModuleDefinitionName": "VirtualMachines",
"ResourceGroupName": "${Parameters.ModuleConfigurationParameters.citrixStoreFrontVMs.ResourceGroup}",
"DependsOn": [
"VirtualNetworkSPOKE",
"DiagnosticStorageAccount",
"LogAnalytics",
"KeyVault",
"ArtifactsStorageAccount",
"JumpboxASG"
],
"Comments": "Creates base citrix storefront servers.",
"Deployment": {
"OverrideParameters": {
"virtualMachineName": {
"value": "${Parameters.ModuleConfigurationParameters.citrixStoreFrontVMs.Windows.Name}"
},
"virtualMachineSize": {
"value": "${Parameters.ModuleConfigurationParameters.citrixStoreFrontVMs.Windows.VMSize}"
},
"virtualMachineOSImage": {
"value": "${Parameters.ModuleConfigurationParameters.citrixStoreFrontVMs.Windows.OSImage}"
},
"virtualMachineOSType": {
"value": "${Parameters.ModuleConfigurationParameters.citrixStoreFrontVMs.Windows.OSType}"
},
"virtualMachineCount": {
"value": "${Parameters.ModuleConfigurationParameters.citrixStoreFrontVMs.Windows.VMCount}"
},
"workspaceId": {
"value": "reference(LogAnalytics.logAnalyticsWorkspaceId)"
},
"logAnalyticsWorkspaceId": {
"value": "reference(LogAnalytics.logAnalyticsWorkspaceResourceId)"
},
"logAnalyticsWorkspacePrimarySharedKey": {
"value": "reference(LogAnalytics.logAnalyticsPrimarySharedKey)"
},
"diagnosticStorageAccountId": {
"value": "reference(DiagnosticStorageAccount.storageAccountResourceId)"
},
"diagnosticStorageAccountName": {
"value": "reference(DiagnosticStorageAccount.storageAccountName)"
},
"diagnosticStorageAccountSasToken": {
"value": "reference(DiagnosticStorageAccount.storageAccountSasToken)"
},
"artifactsStorageAccountKey": {
"value": "reference(ArtifactsStorageAccount.storageAccountAccessKey)"
},
"artifactsStorageAccountName": {
"value": "reference(ArtifactsStorageAccount.storageAccountName)"
},
"artifactsStorageAccountSasKey": {
"value": "reference(ArtifactsStorageAccount.storageAccountSasToken)"
},
"vNetId": {
"value": "reference(VirtualNetworkSPOKE.vNetResourceId)"
},
"subnetName": {
"value": "${Parameters.ModuleConfigurationParameters.citrixStoreFrontVMs.SubnetName}"
},
"applicationSecurityGroupId": {
"value": "reference(JumpboxASG.applicationSecurityGroupResourceId)"
},
"adminUsername": {
"value": "${Parameters.ModuleConfigurationParameters.citrixStoreFrontVMs.AdminUsername}"
},
"adminPassword": {
"reference": {
"keyVault": {
"id": "reference(KeyVault.keyVaultResourceId)"
},
"secretName": "${Parameters.ModuleConfigurationParameters.KeyVault.SecretsObject.Secrets[1].secretName}"
}
},
"storageBlobUrl": {
"value": "${Parameters.ModuleConfigurationParameters.citrixStoreFrontVMs.StorageBlobUrl}"
}
}
}
},
{
"Name": "xenDesktopBrokerVMs",
"ModuleDefinitionName": "VirtualMachines",
"ResourceGroupName": "${Parameters.ModuleConfigurationParameters.xenDesktopBrokerVMs.ResourceGroup}",
"DependsOn": [
"VirtualNetworkSPOKE",
"DiagnosticStorageAccount",
"LogAnalytics",
"KeyVault",
"ArtifactsStorageAccount",
"JumpboxASG"
],
"Comments": "Creates base xen Desktop Broker vms.",
"Deployment": {
"OverrideParameters": {
"virtualMachineName": {
"value": "${Parameters.ModuleConfigurationParameters.xenDesktopBrokerVMs.Windows.Name}"
},
"virtualMachineSize": {
"value": "${Parameters.ModuleConfigurationParameters.xenDesktopBrokerVMs.Windows.VMSize}"
},
"virtualMachineOSImage": {
"value": "${Parameters.ModuleConfigurationParameters.xenDesktopBrokerVMs.Windows.OSImage}"
},
"virtualMachineOSType": {
"value": "${Parameters.ModuleConfigurationParameters.xenDesktopBrokerVMs.Windows.OSType}"
},
"virtualMachineCount": {
"value": "${Parameters.ModuleConfigurationParameters.xenDesktopBrokerVMs.Windows.VMCount}"
},
"workspaceId": {
"value": "reference(LogAnalytics.logAnalyticsWorkspaceId)"
},
"logAnalyticsWorkspaceId": {
"value": "reference(LogAnalytics.logAnalyticsWorkspaceResourceId)"
},
"logAnalyticsWorkspacePrimarySharedKey": {
"value": "reference(LogAnalytics.logAnalyticsPrimarySharedKey)"
},
"diagnosticStorageAccountId": {
"value": "reference(DiagnosticStorageAccount.storageAccountResourceId)"
},
"diagnosticStorageAccountName": {
"value": "reference(DiagnosticStorageAccount.storageAccountName)"
},
"diagnosticStorageAccountSasToken": {
"value": "reference(DiagnosticStorageAccount.storageAccountSasToken)"
},
"artifactsStorageAccountKey": {
"value": "reference(ArtifactsStorageAccount.storageAccountAccessKey)"
},
"artifactsStorageAccountName": {
"value": "reference(ArtifactsStorageAccount.storageAccountName)"
},
"artifactsStorageAccountSasKey": {
"value": "reference(ArtifactsStorageAccount.storageAccountSasToken)"
},
"vNetId": {
"value": "reference(VirtualNetworkSPOKE.vNetResourceId)"
},
"subnetName": {
"value": "${Parameters.ModuleConfigurationParameters.xenDesktopBrokerVMs.SubnetName}"
},
"applicationSecurityGroupId": {
"value": "reference(JumpboxASG.applicationSecurityGroupResourceId)"
},
"adminUsername": {
"value": "${Parameters.ModuleConfigurationParameters.xenDesktopBrokerVMs.AdminUsername}"
},
"adminPassword": {
"reference": {
"keyVault": {
"id": "reference(KeyVault.keyVaultResourceId)"
},
"secretName": "${Parameters.ModuleConfigurationParameters.KeyVault.SecretsObject.Secrets[1].secretName}"
}
},
"storageBlobUrl": {
"value": "${Parameters.ModuleConfigurationParameters.xenDesktopBrokerVMs.StorageBlobUrl}"
}
}
}
},
{
"Name": "CTXSQLCluster",
"ModuleDefinitionName": "VirtualMachines",
"ResourceGroupName": "${Parameters.ModuleConfigurationParameters.CTXSQLCluster.ResourceGroup}",
"DependsOn": [
"VirtualNetworkSPOKE",
"DiagnosticStorageAccount",
"LogAnalytics",
"KeyVault",
"ArtifactsStorageAccount",
"JumpboxASG"
],
"Comments": "Creates X number of servers to be setup as sql cluster.",
"Deployment": {
"OverrideParameters": {
"virtualMachineName": {
"value": "${Parameters.ModuleConfigurationParameters.CTXSQLCluster.Windows.Name}"
},
"virtualMachineSize": {
"value": "${Parameters.ModuleConfigurationParameters.CTXSQLCluster.Windows.VMSize}"
},
"virtualMachineOSImage": {
"value": "${Parameters.ModuleConfigurationParameters.CTXSQLCluster.Windows.OSImage}"
},
"virtualMachineOSType": {
"value": "${Parameters.ModuleConfigurationParameters.CTXSQLCluster.Windows.OSType}"
},
"virtualMachineCount": {
"value": "${Parameters.ModuleConfigurationParameters.CTXSQLCluster.Windows.VMCount}"
},
"workspaceId": {
"value": "reference(LogAnalytics.logAnalyticsWorkspaceId)"
},
"logAnalyticsWorkspaceId": {
"value": "reference(LogAnalytics.logAnalyticsWorkspaceResourceId)"
},
"logAnalyticsWorkspacePrimarySharedKey": {
"value": "reference(LogAnalytics.logAnalyticsPrimarySharedKey)"
},
"diagnosticStorageAccountId": {
"value": "reference(DiagnosticStorageAccount.storageAccountResourceId)"
},
"diagnosticStorageAccountName": {
"value": "reference(DiagnosticStorageAccount.storageAccountName)"
},
"diagnosticStorageAccountSasToken": {
"value": "reference(DiagnosticStorageAccount.storageAccountSasToken)"
},
"artifactsStorageAccountKey": {
"value": "reference(ArtifactsStorageAccount.storageAccountAccessKey)"
},
"artifactsStorageAccountName": {
"value": "reference(ArtifactsStorageAccount.storageAccountName)"
},
"artifactsStorageAccountSasKey": {
"value": "reference(ArtifactsStorageAccount.storageAccountSasToken)"
},
"vNetId": {
"value": "reference(VirtualNetworkSPOKE.vNetResourceId)"
},
"subnetName": {
"value": "${Parameters.ModuleConfigurationParameters.CTXSQLCluster.SubnetName}"
},
"applicationSecurityGroupId": {
"value": "reference(JumpboxASG.applicationSecurityGroupResourceId)"
},
"adminUsername": {
"value": "${Parameters.ModuleConfigurationParameters.CTXSQLCluster.AdminUsername}"
},
"adminPassword": {
"reference": {
"keyVault": {
"id": "reference(KeyVault.keyVaultResourceId)"
},
"secretName": "${Parameters.ModuleConfigurationParameters.KeyVault.SecretsObject.Secrets[1].secretName}"
}
},
"storageBlobUrl": {
"value": "${Parameters.ModuleConfigurationParameters.CTXSQLCluster.StorageBlobUrl}"
}
}
}
},
{
"Name": "CTXDesktopServers",
"ModuleDefinitionName": "VirtualMachines",
"ResourceGroupName": "${Parameters.ModuleConfigurationParameters.CTXDesktopServers.ResourceGroup}",
"DependsOn": [
"VirtualNetworkSPOKE",
"DiagnosticStorageAccount",
"LogAnalytics",
"KeyVault",
"ArtifactsStorageAccount",
"JumpboxASG"
],
"Comments": "Creates X number of servers to be setup as sql cluster.",
"Deployment": {
"OverrideParameters": {
"virtualMachineName": {
"value": "${Parameters.ModuleConfigurationParameters.CTXDesktopServers.Windows.Name}"
},
"virtualMachineSize": {
"value": "${Parameters.ModuleConfigurationParameters.CTXDesktopServers.Windows.VMSize}"
},
"virtualMachineOSImage": {
"value": "${Parameters.ModuleConfigurationParameters.CTXDesktopServers.Windows.OSImage}"
},
"virtualMachineOSType": {
"value": "${Parameters.ModuleConfigurationParameters.CTXDesktopServers.Windows.OSType}"
},
"virtualMachineCount": {
"value": "${Parameters.ModuleConfigurationParameters.CTXDesktopServers.Windows.VMCount}"
},
"workspaceId": {
"value": "reference(LogAnalytics.logAnalyticsWorkspaceId)"
},
"logAnalyticsWorkspaceId": {
"value": "reference(LogAnalytics.logAnalyticsWorkspaceResourceId)"
},
"logAnalyticsWorkspacePrimarySharedKey": {
"value": "reference(LogAnalytics.logAnalyticsPrimarySharedKey)"
},
"diagnosticStorageAccountId": {
"value": "reference(DiagnosticStorageAccount.storageAccountResourceId)"
},
"diagnosticStorageAccountName": {
"value": "reference(DiagnosticStorageAccount.storageAccountName)"
},
"diagnosticStorageAccountSasToken": {
"value": "reference(DiagnosticStorageAccount.storageAccountSasToken)"
},
"artifactsStorageAccountKey": {
"value": "reference(ArtifactsStorageAccount.storageAccountAccessKey)"
},
"artifactsStorageAccountName": {
"value": "reference(ArtifactsStorageAccount.storageAccountName)"
},
"artifactsStorageAccountSasKey": {
"value": "reference(ArtifactsStorageAccount.storageAccountSasToken)"
},
"vNetId": {
"value": "reference(VirtualNetworkSPOKE.vNetResourceId)"
},
"subnetName": {
"value": "${Parameters.ModuleConfigurationParameters.CTXDesktopServers.SubnetName}"
},
"applicationSecurityGroupId": {
"value": "reference(JumpboxASG.applicationSecurityGroupResourceId)"
},
"adminUsername": {
"value": "${Parameters.ModuleConfigurationParameters.CTXDesktopServers.AdminUsername}"
},
"adminPassword": {
"reference": {
"keyVault": {
"id": "reference(KeyVault.keyVaultResourceId)"
},
"secretName": "${Parameters.ModuleConfigurationParameters.KeyVault.SecretsObject.Secrets[1].secretName}"
}
},
"storageBlobUrl": {
"value": "${Parameters.ModuleConfigurationParameters.CTXDesktopServers.StorageBlobUrl}"
}
}
}
}
]
}

Просмотреть файл

@ -245,7 +245,7 @@
"ArtifactsStorageAccount": "file(../_Common/artifactsStorageAccount.json)",
"Jumpbox": {
"ResourceGroup": "${Parameters.InstanceName}-jumpbox-rg",
"AdminUsername": "${Parameters.ModuleConfigurationParameters.KeyVault.SecretsObject.Secrets[0].secretName}",
"AdminUsername": "env(ADMIN_USER_NAME)",
"SubnetName": "${Parameters.ModuleConfigurationParameters.VirtualNetworkSPOKE.Subnets[0].name}",
"StorageBlobUrl": "${Parameters.StorageBlobUrl}",
"Windows": {
@ -265,7 +265,81 @@
"Destination": "HSM"
}
}
},
"citrixStoreFrontVMs": {
"ResourceGroup": "${Parameters.InstanceName}-storefront-rg",
"AdminUsername": "env(ADMIN_USER_NAME)",
"SubnetName": "${Parameters.ModuleConfigurationParameters.VirtualNetworkSPOKE.Subnets[0].name}",
"StorageBlobUrl": "${Parameters.StorageBlobUrl}",
"Windows": {
"Comments": "Windows VM name cannot exceed 13 characters",
"Name": "ctx-sf-vm",
"VMCount": 2,
"OSType": "Windows",
"VMSize": "Standard_DS2_v2",
"OSImage": {
"offer": "WindowsServer",
"publisher": "MicrosoftWindowsServer",
"sku": "2016-Datacenter"
}
}
},
"xenDesktopBrokerVMs": {
"ResourceGroup": "${Parameters.InstanceName}-broker-rg",
"AdminUsername": "env(ADMIN_USER_NAME)",
"SubnetName": "${Parameters.ModuleConfigurationParameters.VirtualNetworkSPOKE.Subnets[0].name}",
"StorageBlobUrl": "${Parameters.StorageBlobUrl}",
"Windows": {
"Comments": "Windows VM name cannot exceed 13 characters",
"Name": "ctx-brker-vm",
"VMCount": 2,
"OSType": "Windows",
"VMSize": "Standard_DS2_v2",
"OSImage": {
"offer": "WindowsServer",
"publisher": "MicrosoftWindowsServer",
"sku": "2016-Datacenter"
}
}
},
"CTXSQLCluster": {
"ResourceGroup": "${Parameters.InstanceName}-ctxsql-rg",
"AdminUsername": "env(ADMIN_USER_NAME)",
"SubnetName": "${Parameters.ModuleConfigurationParameters.VirtualNetworkSPOKE.Subnets[0].name}",
"StorageBlobUrl": "${Parameters.StorageBlobUrl}",
"Windows": {
"Comments": "Windows VM name cannot exceed 13 characters",
"Name": "ctx-sql-vm",
"VMCount": 3,
"OSType": "Windows",
"VMSize": "Standard_DS2_v2",
"OSImage": {
"offer": "WindowsServer",
"publisher": "MicrosoftWindowsServer",
"sku": "2016-Datacenter"
}
}
},
"CTXDesktopServers": {
"ResourceGroup": "${Parameters.InstanceName}-ctxdesktops-rg",
"AdminUsername": "env(ADMIN_USER_NAME)",
"SubnetName": "${Parameters.ModuleConfigurationParameters.VirtualNetworkSPOKE.Subnets[0].name}",
"StorageBlobUrl": "${Parameters.StorageBlobUrl}",
"Windows": {
"Comments": "Windows VM name cannot exceed 13 characters",
"Name": "ctx-dsk-vm",
"VMCount": 3,
"OSType": "Windows",
"VMSize": "Standard_DS2_v2",
"OSImage": {
"offer": "WindowsServer",
"publisher": "MicrosoftWindowsServer",
"sku": "2016-Datacenter"
}
}
}
}
}

Просмотреть файл

@ -0,0 +1,5 @@
{
"Subscriptions": "env(VDC_SUBSCRIPTIONS)",
"Parameters": "file(./parameters.json)",
"Orchestration": "file(./orchestration.json)"
}

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -0,0 +1,901 @@
{
"Organization": "env(ORGANIZATION_NAME)",
"DeploymentName": "shrdsvcs-DC",
"InstanceName": "${Parameters.Organization}-${Parameters.DeploymentName}",
"Subscription": "SPOKE",
"Location": "env(AZURE_LOCATION)",
"EnvironmentName": "env(AZURE_ENVIRONMENT_NAME)",
"StorageBlobUrl": "env(AZURE_STORAGE_BLOB_URL)",
"AzureSentinel": "env(AZURE_SENTINEL)",
"ModuleConfigurationParameters": {
"SharedServices": {
"DeploymentName": "shrdsvcs",
"ActiveDirectory": {
"VmIpAddressStart": [ "172.0.0.10" ]
},
"VirtualNetworkHUB": {
"Id": "/subscriptions/${Subscriptions.SharedServices.SubscriptionId}/resourceGroups/${Parameters.ModuleConfigurationParameters.SharedServices.VirtualNetworkHUB.ResourceGroupName}/providers/Microsoft.Network/virtualNetworks/${Parameters.ModuleConfigurationParameters.SharedServices.VirtualNetworkHUB.Name}",
"Name": "${Parameters.Organization}-shrdsvcs-vnet",
"ResourceGroupName": "${Parameters.organization}-shrdsvcs-network-rg",
"AddressPrefix": "172.0.0.0/16",
"NetworkVirtualAppliance": {
"AzureFirewall": {
"Name": "${Parameters.Organization}-${Parameters.ModuleConfigurationParameters.SharedServices.DeploymentName}-azfw"
}
}
}
},
"OnPremisesInformation": {
"InstanceName": "${Parameters.InstanceName}",
"Comments": "This InstanceName is a temporal value, this value is used in artifactsStorageAccount.json, the idea is to have a global set of services and this name should point to the InstanceName (deployment name) of the global services archetype"
},
"KeyVaultManagementUserId": "env(KEYVAULT_MANAGEMENT_USER_ID)",
"DevOpsServicePrincipalId": "env(DEVOPS_SERVICE_PRINCIPAL_USER_ID)",
"Comments": "Deployment UserId corresponds to the AAD Object Id of the user that wants to manage Key Vault through the Portal. DevOpsServicePrincipalId corresponds the service principal (SPN) - Object Id - of your service connection from Azure DevOps pipeline, to get this value run the following az cli command: az ad sp show --id YOUR_APP_ID --query 'objectId'",
"DiagnosticStorageAccount": {
"Name": "${Parameters.Organization}${Parameters.DeploymentName}diag01",
"ResourceGroup": "${Parameters.InstanceName}-diagnostics-rg",
"Location": "${Parameters.Location}",
"Sku": "Standard_GRS",
"NetworkAcls": {
"bypass": "AzureServices",
"defaultAction": "Deny",
"virtualNetworkRules": [
{
"subnet": "${Parameters.ModuleConfigurationParameters.VirtualNetwork.Subnets[0].Name}"
}
],
"ipRules": []
},
"Policies": {
"Effect": "Audit"
}
},
"LogAnalytics": {
"Name": "${Parameters.InstanceName}-la",
"Comments": "Log Analytics and Diagnostic Storage Account must be deployed in the same region",
"ResourceGroup": "${Parameters.InstanceName}-diagnostics-rg",
"Location": "${Parameters.ModuleConfigurationParameters.DiagnosticStorageAccount.Location}",
"ListOfAllowedRegions": [
"Australia Central",
"Australia East",
"Australia Southeast",
"Canada Central",
"Central India",
"Central US",
"East Asia",
"East US",
"East US 2",
"France Central",
"Japan East",
"Korea Central",
"North Europe",
"South Central US",
"Southeast Asia",
"UK South",
"West Europe",
"West US",
"West US 2",
"USGov Virginia",
"USGov Iowa",
"USGov Arizona",
"USGov Texas",
"USDoD Central",
"USDoD East"
]
},
"AutomationAccounts": {
"Name": "${Parameters.InstanceName}-automation",
"Comments": "Automation Account and Log Analytics must be deployed in the same region",
"ResourceGroup": "${Parameters.ModuleConfigurationParameters.LogAnalytics.ResourceGroup}",
"Location": "${Parameters.ModuleConfigurationParameters.LogAnalytics.Location}",
"UpdateManagementTimeZone": "America/Chicago",
"ListOfAllowedRegions": [
"Australia Central",
"Australia East",
"Australia Southeast",
"Brazil South",
"Canada Central",
"Central India",
"East US",
"East US 2",
"France Central",
"Japan East",
"Korea Central",
"North Europe",
"South Central US",
"Southeast Asia",
"UK South",
"West Central US",
"West Europe",
"West US 2",
"West US",
"USGov Virginia",
"USGov Iowa",
"USGov Arizona",
"USGov Texas",
"USDoD Central",
"USDoD East"
]
},
"AzureBastion": {
"ResourceGroup": "${Parameters.ModuleConfigurationParameters.VirtualNetwork.ResourceGroup}",
"Name": "${Parameters.InstanceName}-bastion",
"Location": "${Parameters.Location}",
"ListOfAllowedRegions": [
"West US",
"East US",
"West Europe",
"South Central US",
"Australia East",
"Japan East",
"USGov Virginia",
"USGov Iowa",
"USGov Arizona",
"USGov Texas",
"USDoD Central",
"USDoD East"
]
},
"ApplicationSecurityGroups": {
"ResourceGroup": "${Parameters.ModuleConfigurationParameters.VirtualNetwork.ResourceGroup}",
"Jumpbox": {
"Name": "jumpbox-asg"
},
"DomainController": {
"Name": "dc-asg"
}
},
"NetworkSecurityGroups": {
"ResourceGroup": "${Parameters.ModuleConfigurationParameters.VirtualNetwork.ResourceGroup}",
"Comments": "Virtual Network (TCP and UDP) to Application Security Group rules are required for DNS resolution",
"SharedServices": {
"Name": "${Parameters.DeploymentName}-nsg",
"Rules": [
{
"name": "allow-tcp-between-adds",
"properties": {
"access": "Allow",
"destinationAddressPrefixes": [],
"destinationAddressPrefix": "",
"destinationPortRange": "",
"destinationPortRanges": [
"389",
"42",
"88",
"636",
"3268",
"3269",
"445",
"25",
"135",
"5722",
"464",
"9389",
"139",
"53",
"49152-65535"
],
"direction": "Inbound",
"priority": 100,
"protocol": "Tcp",
"sourceAddressPrefix": "",
"sourcePortRange": "*",
"sourcePortRanges": [],
"destinationApplicationSecurityGroups": [
{
"name": "${Parameters.ModuleConfigurationParameters.ApplicationSecurityGroups.DomainController.Name}"
}
],
"sourceApplicationSecurityGroups": [
{
"name": "${Parameters.ModuleConfigurationParameters.ApplicationSecurityGroups.DomainController.Name}"
}
]
}
},
{
"name": "allow-udp-between-adds",
"properties": {
"access": "Allow",
"destinationAddressPrefixes": [],
"destinationAddressPrefix": "",
"destinationPortRange": "",
"destinationPortRanges": [
"389",
"88",
"445",
"123",
"464",
"138",
"137",
"53",
"49152-65535"
],
"destinationApplicationSecurityGroups": [
{
"name": "${Parameters.ModuleConfigurationParameters.ApplicationSecurityGroups.DomainController.Name}"
}
],
"direction": "Inbound",
"priority": 110,
"protocol": "Udp",
"sourceAddressPrefix": "",
"sourcePortRange": "*",
"sourcePortRanges": [],
"sourceApplicationSecurityGroups": [
{
"name": "${Parameters.ModuleConfigurationParameters.ApplicationSecurityGroups.DomainController.Name}"
}
]
}
},
{
"name": "allow-rdp-into-dc",
"properties": {
"access": "Allow",
"destinationAddressPrefixes": [],
"destinationAddressPrefix": "",
"destinationPortRange": "3389",
"destinationPortRanges": [],
"direction": "Inbound",
"priority": 140,
"protocol": "TCP",
"sourceAddressPrefix": "",
"sourcePortRange": "*",
"sourcePortRanges": [],
"destinationApplicationSecurityGroups": [
{
"name": "${Parameters.ModuleConfigurationParameters.ApplicationSecurityGroups.DomainController.Name}"
}
],
"sourceApplicationSecurityGroups": [
{
"name": "${Parameters.ModuleConfigurationParameters.ApplicationSecurityGroups.Jumpbox.Name}"
}
]
}
},
{
"name": "allow-rdp-ssh-into-jb",
"properties": {
"access": "Allow",
"destinationAddressPrefixes": [],
"destinationAddressPrefix": "",
"destinationPortRanges": [
"3389",
"22"
],
"destinationPortRange": "",
"direction": "Inbound",
"priority": 150,
"protocol": "TCP",
"sourceAddressPrefix": "VirtualNetwork",
"sourcePortRange": "*",
"sourcePortRanges": [],
"destinationApplicationSecurityGroups": [
{
"name": "${Parameters.ModuleConfigurationParameters.ApplicationSecurityGroups.Jumpbox.Name}"
}
],
"sourceApplicationSecurityGroups": []
}
},
{
"name": "allow-tcp-vnet-adds",
"properties": {
"access": "Allow",
"destinationAddressPrefixes": [],
"destinationAddressPrefix": "",
"destinationPortRanges": [
"389",
"42",
"88",
"636",
"3268",
"3269",
"445",
"25",
"135",
"5722",
"464",
"9389",
"139",
"53",
"49152-65535"
],
"destinationPortRange": "",
"direction": "Inbound",
"priority": 160,
"protocol": "TCP",
"sourceAddressPrefix": "VirtualNetwork",
"sourcePortRange": "*",
"sourcePortRanges": [],
"destinationApplicationSecurityGroups": [
{
"name": "${Parameters.ModuleConfigurationParameters.ApplicationSecurityGroups.DomainController.Name}"
}
],
"sourceApplicationSecurityGroups": []
}
},
{
"name": "allow-udp-vnet-adds",
"properties": {
"access": "Allow",
"destinationAddressPrefixes": [],
"destinationAddressPrefix": "",
"destinationPortRanges": [
"389",
"88",
"445",
"123",
"464",
"138",
"137",
"53",
"49152-65535"
],
"destinationPortRange": "",
"direction": "Inbound",
"priority": 170,
"protocol": "UDP",
"sourceAddressPrefix": "VirtualNetwork",
"sourcePortRange": "*",
"sourcePortRanges": [],
"destinationApplicationSecurityGroups": [
{
"name": "${Parameters.ModuleConfigurationParameters.ApplicationSecurityGroups.DomainController.Name}"
}
],
"sourceApplicationSecurityGroups": []
}
},
{
"name": "allow-azure-bastion-rdp-into-jb",
"properties": {
"access": "Allow",
"destinationAddressPrefixes": [],
"destinationAddressPrefix": "",
"destinationPortRanges": [
"3389",
"22"
],
"destinationPortRange": "",
"direction": "Inbound",
"priority": 180,
"protocol": "TCP",
"sourceAddressPrefix": "${Parameters.ModuleConfigurationParameters.VirtualNetwork.Subnets[5].addressPrefix}",
"sourcePortRange": "*",
"sourcePortRanges": [],
"destinationApplicationSecurityGroups": [
{
"name": "${Parameters.ModuleConfigurationParameters.ApplicationSecurityGroups.Jumpbox.Name}"
}
],
"sourceApplicationSecurityGroups": []
}
},
{
"name": "deny-vnet",
"properties": {
"access": "Deny",
"destinationAddressPrefix": "VirtualNetwork",
"destinationAddressPrefixes": [],
"destinationPortRange": "*",
"destinationPortRanges": [],
"direction": "Inbound",
"priority": 4096,
"protocol": "*",
"sourceAddressPrefix": "VirtualNetwork",
"sourcePortRange": "*",
"sourcePortRanges": [],
"destinationApplicationSecurityGroups": [],
"sourceApplicationSecurityGroups": []
}
},
{
"name": "allow-vnet",
"properties": {
"access": "Allow",
"destinationAddressPrefix": "*",
"destinationAddressPrefixes": [],
"destinationPortRange": "*",
"destinationPortRanges": [],
"direction": "Outbound",
"priority": 100,
"protocol": "*",
"sourceAddressPrefix": "VirtualNetwork",
"sourcePortRange": "*",
"sourcePortRanges": [],
"destinationApplicationSecurityGroups": [],
"sourceApplicationSecurityGroups": []
}
}
]
},
"DMZ": {
"Name": "dmz-nsg",
"Rules": [
{
"name": "allow-vnet",
"properties": {
"access": "Allow",
"destinationAddressPrefix": "*",
"destinationAddressPrefixes": [],
"destinationPortRange": "*",
"destinationPortRanges": [],
"direction": "Inbound",
"priority": 100,
"protocol": "*",
"sourceAddressPrefix": "VirtualNetwork",
"sourcePortRange": "*",
"sourcePortRanges": [],
"destinationApplicationSecurityGroups": [],
"sourceApplicationSecurityGroups": []
}
}
]
}
},
"RouteTables": {
"ResourceGroup": "${Parameters.ModuleConfigurationParameters.VirtualNetwork.ResourceGroup}",
"SharedServices": {
"Name": "${Parameters.DeploymentName}-udr",
"Routes": [
]
}
},
"VirtualNetwork": {
"Name": "${Parameters.InstanceName}-vnet",
"ResourceGroup": "${Parameters.InstanceName}-network-rg",
"AddressPrefixes": [
"172.30.0.0/16"
],
"EnableDdosProtection": false,
"EnableVmProtection": false,
"Subnets": [
{
"name": "shrdsvcs-dc",
"addressPrefix": "172.30.0.0/24",
"networkSecurityGroupName": "${Parameters.ModuleConfigurationParameters.NetworkSecurityGroups.SharedServices.Name}",
"routeTableName": "${Parameters.ModuleConfigurationParameters.RouteTables.SharedServices.Name}",
"serviceEndpoints": [
{
"service": "Microsoft.EventHub"
},
{
"service": "Microsoft.Sql"
},
{
"service": "Microsoft.Storage"
},
{
"service": "Microsoft.KeyVault"
}
]
},
{
"name": "GatewaySubnet",
"addressPrefix": "172.30.2.0/28",
"networkSecurityGroupName": "",
"routeTableName": "",
"serviceEndpoints": []
},
{
"name": "AccessLayerSubnet",
"addressPrefix": "172.30.3.0/28",
"networkSecurityGroupName": "",
"routeTableName": "",
"serviceEndpoints": []
}
,
{
"name": "ResourceLayerSubnet",
"addressPrefix": "172.30.4.0/28",
"networkSecurityGroupName": "",
"routeTableName": "",
"serviceEndpoints": []
},
{
"name": "ControlLayerSubnet",
"addressPrefix": "172.30.5.0/28",
"networkSecurityGroupName": "",
"routeTableName": "",
"serviceEndpoints": []
},
{
"name": "AzureBastionSubnet",
"addressPrefix": "172.30.6.0/27",
"networkSecurityGroupName": "",
"routeTableName": "",
"serviceEndpoints": []
}
],
"DnsServers": [
"${Parameters.ModuleConfigurationParameters.ActiveDirectory.PrimaryDomainControllerIP}"
]
},
"VirtualNetworkGateway": {
"Name": "${Parameters.InstanceName}-vgw",
"ResourceGroup": "${Parameters.ModuleConfigurationParameters.VirtualNetwork.ResourceGroup}",
"VirtualNetworkGatewayType": "vpn",
"VirtualNetworkGatewaySku": "VpnGw1",
"VpnType": "RouteBased",
"EnableBgp": false,
"VpnSharedKey": "asodgfhjkaw4tu0w9vuijv0qu3409tu",
"LocalConnection": {
"Name": "${Parameters.Organization}-to-onprem"
},
"RemoteConnection": {
"Name": "onprem-to-${Parameters.Organization}"
}
},
"AzureFirewall": {
"Name": "${Parameters.InstanceName}-azfw",
"ResourceGroup": "${Parameters.ModuleConfigurationParameters.VirtualNetwork.ResourceGroup}",
"ApplicationRuleCollections": [
{
"name": "allow-app-rules",
"properties": {
"priority": 100,
"action": {
"type": "allow"
},
"rules": [
{
"name": "allow-ase-tags",
"sourceAddresses": [
"*"
],
"protocols": [
{
"protocolType": "HTTP",
"port": "80"
},
{
"protocolType": "HTTPS",
"port": "443"
}
],
"fqdnTags": [
"AppServiceEnvironment",
"WindowsUpdate"
]
},
{
"name": "allow-ase-management",
"sourceAddresses": [
"*"
],
"protocols": [
{
"protocolType": "HTTP",
"port": "80"
},
{
"protocolType": "HTTPS",
"port": "443"
}
],
"targetFqdns": [
"management.azure.com",
"*.digicert.com",
"*.data.microsoft.com",
"global.metrics.nsatc.net",
"ocsp.msocsp.com"
]
},
{
"name": "allow-sites",
"sourceAddresses": [
"*"
],
"protocols": [
{
"protocolType": "HTTP",
"port": "80"
},
{
"protocolType": "HTTPS",
"port": "443"
}
],
"targetFqdns": [
"*.trafficmanager.net",
"*.azureedge.net",
"*.microsoft.com",
"*.core.windows.net",
"*.windows.com",
"*.opinsights.azure.com",
"*.azure-automation.net",
"*.visualstudio.com",
"*.bing.com",
"*.ubuntu.com",
"api.snapcraft.io",
"api.rubygems.org",
"*.powershellgallery.com",
"powershellgallery.com",
"*.msecnd.net",
"msecnd.net",
"*.nuget.org",
"nuget.org",
"*.azureprofilerfrontdoor.cloudapp.net",
"azureprofilerfrontdoor.cloudapp.net",
"*.download.opensuse.org",
"download.opensuse.org",
"*.monitoring.azure.com",
"monitoring.azure.com"
]
}
]
}
}
],
"NetworkRuleCollections": [
{
"name": "allow-network-rules",
"properties": {
"priority": 100,
"action": {
"type": "allow"
},
"rules": [
{
"name": "allow-ntp",
"sourceAddresses": [
"*"
],
"destinationAddresses": [
"*"
],
"destinationPorts": [
"123",
"12000"
],
"protocols": [
"Any"
]
},
{
"name": "allow-windows-activation-server",
"sourceAddresses": [
"*"
],
"destinationAddresses": [
"23.102.135.246"
],
"destinationPorts": [
"1688"
],
"protocols": [
"TCP"
]
},
{
"name": "allow-udp-adds",
"sourceAddresses": [
"${Parameters.ModuleConfigurationParameters.VirtualNetwork.Subnets[0].addressPrefix}"
],
"destinationAddresses": [
"*"
],
"destinationPorts": [
"*"
],
"protocols": [
"UDP"
]
},
{
"name": "allow-tcp-adds",
"sourceAddresses": [
"${Parameters.ModuleConfigurationParameters.VirtualNetwork.Subnets[0].addressPrefix}"
],
"destinationAddresses": [
"*"
],
"destinationPorts": [
"*"
],
"protocols": [
"TCP"
]
}
]
}
}
]
},
"KeyVault": {
"Name": "${Parameters.InstanceName}-kv",
"ResourceGroup": "${Parameters.InstanceName}-keyvault-rg",
"Sku": "Premium",
"EnableVaultForDeployment": true,
"EnableVaultForDiskEncryption": true,
"EnableVaultForTemplateDeployment": true,
"AccessPolicies": [
{
"tenantId": "${Parameters.TenantId}",
"objectId": "${Parameters.ModuleConfigurationParameters.KeyVaultManagementUserId}",
"permissions": {
"certificates": [
"All"
],
"keys": [
"All"
],
"secrets": [
"All"
]
}
},
{
"tenantId": "${Parameters.TenantId}",
"objectId": "${Parameters.ModuleConfigurationParameters.DevOpsServicePrincipalId}",
"permissions": {
"certificates": [
"All"
],
"keys": [
"All"
],
"secrets": [
"All"
]
}
}
],
"SecretsObject": {
"Comments": "Creating an object so we can use a secretsobject parameter type in our ARM template",
"Secrets": [
{
"secretName": "VM-admin-user-name",
"secretValue": "env(ADMIN_USER_NAME)"
},
{
"secretName": "VM-admin-pw",
"secretValue": "env(ADMIN_USER_PWD)"
},
{
"secretName": "Domain-Admin-Username",
"secretValue": "env(DOMAIN_ADMIN_USERNAME)"
},
{
"secretName": "Domain-Admin-PW",
"secretValue": "env(DOMAIN_ADMIN_USER_PWD)"
},
{
"secretName": "admin-user-ssh",
"secretValue": "env(ADMIN_USER_SSH)"
}
]
},
"NetworkAcls": {
"bypass": "AzureServices",
"defaultAction": "Deny",
"virtualNetworkRules": [
{
"subnet": "${Parameters.ModuleConfigurationParameters.VirtualNetwork.Subnets[0].Name}"
}
],
"ipRules": []
}
},
"ArtifactsStorageAccount": "file(../_Common/artifactsStorageAccount.json)",
"Jumpbox": {
"ResourceGroup": "${Parameters.InstanceName}-jumpbox-rg",
"AdminUsername": "env(ADMIN_USER_NAME)",
"SubnetName": "${Parameters.ModuleConfigurationParameters.VirtualNetwork.Subnets[0].name}",
"StorageBlobUrl": "${Parameters.StorageBlobUrl}",
"Windows": {
"Comments": "Windows VM name cannot exceed 13 characters",
"Name": "win-jb-vm",
"VMCount": 1,
"OSType": "Windows",
"VMSize": "Standard_DS2_v2",
"OSImage": {
"offer": "WindowsServer",
"publisher": "MicrosoftWindowsServer",
"sku": "2016-Datacenter"
},
"Kek": {
"Name": "WindowsJumpboxKey",
"Comments": "Destination can be HSM or Software. Use HSM to create Production keys.",
"Destination": "HSM"
}
},
"Linux": {
"Comments": "Linux VM name cannot exceed 63 characters",
"Name": "linux-jb-vm",
"VMCount": 1,
"OSType": "Linux",
"VMSize": "Standard_DS2_v2",
"OSImage": {
"publisher": "Canonical",
"offer": "UbuntuServer",
"sku": "18.04-LTS",
"version": "latest"
},
"Kek": {
"Name": "LinuxJumpboxKey",
"Comments": "Destination can be HSM or Software. Use HSM to create Production keys.",
"Destination": "HSM"
}
}
},
"ActiveDirectory": {
"Name": "primary-ad",
"ResourceGroup": "${Parameters.InstanceName}-adds-rg",
"Comments": "Windows VM name cannot exceed 13 characters.",
"PrimaryDomainControllerIP": "172.30.0.10",
"DomainName": "contoso.com",
"StorageBlobUrl": "${Parameters.StorageBlobUrl}",
"ADSitename": "Cloud-Site",
"CloudZone": "contosocloud.com",
"DomainAdminUsername": "env(DOMAIN_ADMIN_USERNAME)",
"DomainAdminPassword": {
"keyVault": {
"id": "reference(KeyVault.keyVaultResourceId)"
},
"secretName": "${Parameters.ModuleConfigurationParameters.KeyVault.SecretsObject.Secrets[3].secretName}"
},
"VMSize": "Standard_DS3_v2",
"OSImage": {
"offer": "WindowsServer",
"publisher": "MicrosoftWindowsServer",
"sku": "2016-Datacenter"
},
"OSType": "Windows",
"Kek": {
"Name": "AdKey",
"Comments": "Destination can be HSM or Software. Use HSM to create Production keys.",
"Destination": "HSM"
},
"SubnetName": "${Parameters.ModuleConfigurationParameters.VirtualNetwork.Subnets[0].name}"
},
"ActiveDirectoryDomainServices": {
"Name": "adds-vm",
"ResourceGroup": "${Parameters.ModuleConfigurationParameters.ActiveDirectory.ResourceGroup}",
"Comments": "Windows VM name cannot exceed 13 characters. Additionally, Make sure that AddsIPAddressStart and ActiveDirectory.PrimaryDomainControllerIP are in the same subnet address prefix and they don't overlap",
"StorageBlobUrl": "${Parameters.StorageBlobUrl}",
"AdminUsername": "env(ADMIN_USER_NAME)",
"AdminPassword": {
"keyVault": {
"id": "reference(KeyVault.keyVaultResourceId)"
},
"secretName": "${Parameters.ModuleConfigurationParameters.KeyVault.SecretsObject.Secrets[1].secretName}"
},
"Kek": {
"Name": "AddsKey",
"Comments": "Destination can be HSM or Software. Use HSM to create Production keys.",
"Destination": "HSM"
},
"DomainAdminUsername": "${Parameters.ModuleConfigurationParameters.ActiveDirectory.DomainAdminUsername}",
"DomainAdminPassword": "${Parameters.ModuleConfigurationParameters.ActiveDirectory.DomainAdminPassword}",
"VMCount": 2,
"VMSize": "Standard_DS3_v2",
"OSType": "Windows",
"OSImage": {
"offer": "WindowsServer",
"publisher": "MicrosoftWindowsServer",
"sku": "2016-Datacenter"
},
"AddsIPAddressStart": "172.30.0.20",
"DomainName": "${Parameters.ModuleConfigurationParameters.ActiveDirectory.DomainName}",
"PrimaryDomainControllerIP": "${Parameters.ModuleConfigurationParameters.ActiveDirectory.PrimaryDomainControllerIP}",
"ADSitename": "${Parameters.ModuleConfigurationParameters.ActiveDirectory.ADSitename}",
"DomaincontrollerDriveLetter": "F",
"SubnetName": "${Parameters.ModuleConfigurationParameters.VirtualNetwork.Subnets[0].name}",
"DataDisks": [
{
"size": 127,
"driveLetter": "F",
"diskId": 2
}
]
}
}
}

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -0,0 +1,213 @@
# **To deploy Azure Virtual Datacenter for Shared Services as a SPOKE Environment**
Deployment steps for [SharedServices](../../Environments/SharedServices) archetypes provided in the toolkit.
The documentation applies to manually building and running the docker instance. For github action setup click
"HERE."
### Clone the repository
These steps assume that the `git` command is on your path.
1. Open a terminal window
2. Navigate to a folder where you want to store the source for the toolkit. For, e.g. `c:\git`, navigate to that folder.
3. Run `git clone https://github.com/RKSelvi/vdc.git`. This will clone the GitHub repository in a folder named `vdc`.
4. Run `cd vdc` to change directory in the source folder.
5. Run `git checkout master` to switch to the branch with the current in-development version of the toolkit.
### Build the Docker image
1. Ensure that the [Docker daemon](https://docs.docker.com/config/daemon/) is running. If you are new to Docker, the easiest way to do this is to install [Docker Desktop](https://www.docker.com/products/docker-desktop).
1. Open a terminal window
1. Navigate to the folder where you cloned the repository. _The rest of the quickstart assumes that this path is `C:\git\vdc\`_
1. Run `docker build . -t vdc:latest` to build the image.
### Run the toolkit container
After the image finishing building, you can run it using:
`docker run -it --entrypoint="pwsh" --rm -v C:\git\vdc\Config:/usr/src/app/Config -v C:\git\vdc\Environments:/usr/src/app/Environments -v C:\git\vdc\Modules:/usr/src/app/Modules vdc:latest`
A few things to note:
- You don't need to build the image every time you want to run the container. You'll only need to rebuild it if there are changes to the source (primarily changes in the `Orchestration` folder).
- The `docker run` command above will map volumes in the container to volumes on the host machine. This will allow you to directly modify files in these directories (`Config`,`Environments`, and `Modules`).
When the container starts, you will see the prompt
`PS /usr/src/app>`
## Configure the toolkit
To configure the toolkit for this quickstart, we will need to collect the following information.
You'll need:
- A subscription for the toolkit to use for logging and tracking deployment.
- The associated tenant id of the Azure Active Directory associated with those subscriptions.
- The object id of the user account that you'll use to run the deployment.
- The object id of a [service principal](https://docs.microsoft.com/azure/active-directory/develop/howto-create-service-principal-portal) that Azure DevOps can use for deployment. This is only for CI/CD pipeline
- An organization name for generating a prefix for naming resources.
- The desired username and password for the Active Directory domain admin that will be created. Active Directory is not deployed now.
- The desired password of the Windows jumpbox.
- The [public ssh key](https://docs.microsoft.com/azure/virtual-machines/linux/mac-create-ssh-keys) for accessing the Linux jumpbox.
Note: You can use a single subscription. You'll just need to provide the same subscription id in multiple locations in the configuration.
### Collecting user object id and tentant id
You can get your user object id and tenant id in the portal or by using command line utitilies.
Using Azure PowerShell:
1. Run `Connect-AzAccount -Tenant "[TENANT_ID]" -SubscriptionId "[SUBSCRIPTION_ID]" -EnvironmentName "[AZURE_ENVIRONMENT]"` to login and set an Azure context.
1. Run `Get-AzContext | % { Get-AzADUser -UserPrincipalName $($_.Account.Id) } | select Id` to get the user object id.
1. Run `Get-AzContext | select Tenant` to get the tenant id.
### Setting the configuration
To deploy the Shared Services environment, you will need to modify two configuration files and set several environmental variables.
#### [`Config\toolkit.subscription.json`](../Config/toolkit.subscription.json)
This file is for toolkit configuration in general.
- Set `Subscription.TenantId` to the tenant id note above.
- Set `Subscription.SubscriptionId` to the id of the subscription used for logging and deployment state tracking noted above.
- Set `Subscription.Location` for Azure regions. For e.g. for Azure Gov, "USGov Virginia"
#### [`Environments\_Common\subscriptions.json`](../Environments/_Common/subscriptions.json)
This file is for the deployment enviroments configuration. Update the subscription & tenant id's in the following environments in the subscriptions.json, `VDCVDI`, `SharedServices` & `Artifacts`
- Set `TenantId` to the tenant id note above.
- Set `SubscriptionId` to the id of the target subscription for the deployment noted above.
#### Environmental variables
The toolkit uses environmental variables instead of configuration files to help avoid the accidental inclusion of secrets into your source control. In the context of a CI/CD pipeline, these values would be retrieved from a key vault.
Set these environmental variables by substituting the actual values in the script below.
Copy and paste this script into PowerShell to execute it.
Note: The first two variables are set with the content of the configuration files we just modified. The path will not resolve correctly unless you are in `/usr/src/app` directory.
```PowerShell
$ENV:ORGANIZATION_NAME = "[ORGANIZATION_NAME]"
$ENV:AZURE_ENVIRONMENT_NAME = "[AZURE_ENVIRONMENT]"
$ENV:AZURE_LOCATION = "[AZURE_REGION]"
$ENV:TENANT_ID = "[TENANT_ID]"
$ENV:SUBSCRIPTION_ID = "[SUBSCRIPTION_ID]"
$ENV:KEYVAULT_MANAGEMENT_USER_ID = "[KEY_VAULT_MANAGEMENT_USER_ID]"
$ENV:DEVOPS_SERVICE_PRINCIPAL_USER_ID = "[SERVICE_PRINCIPAL_USER_ID]"
$ENV:DOMAIN_ADMIN_USERNAME = "[DOMAIN_ADMIN_USER_NAME]"
$ENV:DOMAIN_ADMIN_USER_PWD = "[DOMAIM_ADMIN_USER_PASSWORD]"
$ENV:ADMIN_USER_NAME = "[VM_ADMIN_USER_NAME]"
$ENV:ADMIN_USER_PWD = "[VM_ADMIN_USER_PASSWORD]"
$ENV:AZURE_DISCOVERY_URL = "https://management.azure.com/metadata/endpoints?api-version=2019-05-01"
$ENV:ADMIN_USER_SSH = "[SSH_KEY]"
$ENV:AZURE_SENTINEL = "[BOOLEAN]
```
**NOTE:** Examples to setting the env variables
- "[ORGANIZATION_NAME]"
- Abbreviation of your org (for e.g. contoso) with **NO SPACES**
- Must be 10 characters or less
- "[AZURE_ENVIRONMENT]"
- For Azure Commercial
- "AzureCloud"
- For Azure Government
- "AzureUSGovernment"
- "[AZURE_REGION]" - Depending on the Azure Enviroment, provide Azure regions. For e.g.
- Azure public cloud
- "East US"
- "East US 2"
- Azure Government
- "USGov Virginia"
- "USGov Iowa"
- "[KEY_VAULT_MANAGEMENT_USER_ID]"
- User's GUID from AAD deploying the VDC toolkit
- "[SERVICE_PRINCIPAL_USER_ID]"
- Used by CI/CD (not yet implemented). Can be same as KEY_VAULT_MANAGEMENT_USER_ID
- "[TENANT_ID]" - Tenant's GUID
- "[SUBSCRIPTION_ID]" - Subscription's GUID
- "[DOMAIN_ADMIN_USER_NAME]"
- Domain user name - will be used for AD deployment and not yet included in current deployment
- "[DOMAIM_ADMIN_USER_PASSWORD]"
- Domain user password - will be used for AD deployment and not yet included in current deployment
- "[VM_ADMIN_USER_NAME]"
- VM log in username
- "[VM_ADMIN_USER_PASSWORD]"
- VM user password
- "[SSH_KEY]"
- Needs to be a valid public ssh rsa key for SSH to linux box
- "[BOOLEAN]
- This value needs to be "True" or "False"
- "True" will deploy Azure Sentinel to the Shared Services Environment
- "False" will NOT deploy Azure Sentinel
To use the above script:
1. Return to the running Docker container from earlier in the quickstart.
2. Confirm that you are in the `/usr/src/app` directory.
3. Make a copy of the above script and replace the necessary values.
4. Copy the script into the clipboard and paste it in the terminal.
5. Verify that the enviromental variables are set by running `env` to view the current values.
#### Parameters
Any application specific parameters updates should be done in the [parameters.json](../../Environments/SharedServices/parameters.json) file such as IP address, subnet names, subnet range, secrets etc.
## Deploying the Shared Services environment
1. Return to the running Docker container from earlier in the quickstart.
1. If you have not already done so, run `Connect-AzAccount -Tenant "[TENANT_ID]" -SubscriptionId "[SUBSCRIPTION_ID]" -EnvironmentName "[AZURE_ENVIRONMENT]"` to login and set an Azure context.
1. To deploy the entire Shared Services environment, you can run a single command:
``` PowerShell
./Orchestration/OrchestrationService/ModuleConfigurationDeployment.ps1 -DefinitionPath ./Environments/SharedServices/definition.json
```
The toolkit will begin deploying the constituent modules and the status will be sent to the terminal.
Open the [Azure portal](https://portal.azure.us) and you can check the status of the invididual deployments. Azure portal link will be based on azure environment.
## Deploying individual modules
If you prefer you can deploy the constituent modules for Shared Services individually.
The following is the series of commands to execute.
``` PowerShell
.\Orchestration\OrchestrationService\ModuleConfigurationDeployment.ps1 -DefinitionPath .\Environments\SharedServices\definition.json -ModuleConfigurationName "AzureFirewall"
.\Orchestration\OrchestrationService\ModuleConfigurationDeployment.ps1 -DefinitionPath .\Environments\SharedServices\definition.json -ModuleConfigurationName "VirtualNetwork"
.\Orchestration\OrchestrationService\ModuleConfigurationDeployment.ps1 -DefinitionPath .\Environments\SharedServices\definition.json -ModuleConfigurationName "AzureSecurityCenter"
.\Orchestration\OrchestrationService\ModuleConfigurationDeployment.ps1 -DefinitionPath .\Environments\SharedServices\definition.json -ModuleConfigurationName "NISTControls"
.\Orchestration\OrchestrationService\ModuleConfigurationDeployment.ps1 -DefinitionPath .\Environments\SharedServices\definition.json -ModuleConfigurationName "AutomationAccounts"
.\Orchestration\OrchestrationService\ModuleConfigurationDeployment.ps1 -DefinitionPath .\Environments\SharedServices\definition.json -ModuleConfigurationName "DomainControllerASG"
.\Orchestration\OrchestrationService\ModuleConfigurationDeployment.ps1 -DefinitionPath .\Environments\SharedServices\definition.json -ModuleConfigurationName "DiagnosticStorageAccount"
.\Orchestration\OrchestrationService\ModuleConfigurationDeployment.ps1 -DefinitionPath .\Environments\SharedServices\definition.json -ModuleConfigurationName "EnableServiceEndpointOnDiagnosticStorageAccount"
.\Orchestration\OrchestrationService\ModuleConfigurationDeployment.ps1 -DefinitionPath .\Environments\SharedServices\definition.json -ModuleConfigurationName "LogAnalytics"
.\Orchestration\OrchestrationService\ModuleConfigurationDeployment.ps1 -DefinitionPath .\Environments\SharedServices\definition.json -ModuleConfigurationName "KeyVault"
.\Orchestration\OrchestrationService\ModuleConfigurationDeployment.ps1 -DefinitionPath .\Environments\SharedServices\definition.json -ModuleConfigurationName "ArtifactsStorageAccount"
.\Orchestration\OrchestrationService\ModuleConfigurationDeployment.ps1 -DefinitionPath .\Environments\SharedServices\definition.json -ModuleConfigurationName "UploadScriptsToArtifactsStorage"
.\Orchestration\OrchestrationService\ModuleConfigurationDeployment.ps1 -DefinitionPath .\Environments\SharedServices\definition.json -ModuleConfigurationName "JumpboxASG"
.\Orchestration\OrchestrationService\ModuleConfigurationDeployment.ps1 -DefinitionPath .\Environments\SharedServices\definition.json -ModuleConfigurationName "SharedServicesNSG"
```
**NOTE:**
1. If deployment reports, unable to find deployment storage account, it could be that PowerShell is not connected to Azure.
2. Open a new PowerShell/Docker instance if there was any changes to files in Environments folder
### **Teardown the environment**
``` PowerShell
./Orchestration/OrchestrationService/ModuleConfigurationDeployment.ps1 -TearDownEnvironment -DefinitionPath ./Environments/SharedServices/definition.json
```
Note: This is the same command you used to deploy except that you include ` -TearDownEnvironment`.
It uses the same configuration, so if you change the configuration the tear down may not execute as expected.
For safety reasons, the key vault will not be deleted. Instead, it will be set to a _removed_ state. This means that the name is still considered in use. To fully delete the key vault, use:
``` PowerShell
Get-AzKeyVault -InRemovedState | ? { Write-Host "Removing vault: $($_.VaultName)"; Remove-AzKeyVault -InRemovedState -VaultName $_.VaultName -Location $_.Location -Force }
```

Просмотреть файл

@ -2,7 +2,8 @@
Deployment steps for [SharedServices](../../Environments/SharedServices) archetypes provided in the toolkit.
The documentation applies to manually building and running the docker instance. For github action setup click
[GitHub Action for VDC](../../.github/workflows/README.md)
[GitHub Action for VDC](../../.github/workflows/README.md). For the NEW master orchestration refer to [Master Orchestration](../../Docs/masterOrchestration)
### Clone the repository
@ -14,7 +15,14 @@ These steps assume that the `git` command is on your path.
4. Run `cd vdc` to change directory in the source folder.
5. Run `git checkout master` to switch to the branch with the current in-development version of the toolkit.
### Build the Docker image
### Download Dependencies to your workstation
1. Download PowerShell 7.0.1 or later - [PS 7 Download](https://github.com/PowerShell/PowerShell/releases)
2. Download DOT NET 2.2 - [DOTNET 2.2](https://dotnet.microsoft.com/download/dotnet-core/2.2)
3. Open the VDC toolkit to the root directory
## Docker setup is ONLY required if you do not use PowerShell 7 on your workstation
### Build the Docker image
1. Ensure that the [Docker daemon](https://docs.docker.com/config/daemon/) is running. If you are new to Docker, the easiest way to do this is to install [Docker Desktop](https://www.docker.com/products/docker-desktop).
1. Open a terminal window
@ -76,7 +84,9 @@ $ENV:ORGANIZATION_NAME = "[ORGANIZATION_NAME]"
$ENV:AZURE_ENVIRONMENT_NAME = "[AZURE_ENVIRONMENT]"
$ENV:AZURE_LOCATION = "[AZURE_REGION]"
$ENV:TENANT_ID = "[TENANT_ID]"
$ENV:SUBSCRIPTION_ID = "[SUBSCRIPTION_ID]"
$ENV:SUBSCRIPTION_ID = "[SPOKE_SUBSCRIPTION_ID]"
$ENV:HUB_SUB_ID = "[HUB_SUBSCRIPTION_ID]"
$ENV:ARTIFACT_LOCATION = "[ARTIFACT_STORAGE_LOCATION]"
$ENV:KEYVAULT_MANAGEMENT_USER_ID = "[KEY_VAULT_MANAGEMENT_USER_ID]"
$ENV:DEVOPS_SERVICE_PRINCIPAL_USER_ID = "[SERVICE_PRINCIPAL_USER_ID]"
$ENV:DOMAIN_ADMIN_USERNAME = "[DOMAIN_ADMIN_USER_NAME]"
@ -110,7 +120,15 @@ $ENV:AZURE_SENTINEL = "[BOOLEAN]"
- "[SERVICE_PRINCIPAL_USER_ID]"
- Used by CI/CD (not yet implemented). Can be same as KEY_VAULT_MANAGEMENT_USER_ID
- "[TENANT_ID]" - Tenant's GUID
- "[SUBSCRIPTION_ID]" - Subscription's GUID
- "[SPOKE_SUBSCRIPTION_ID]" - Subscription's GUID
- Spoke environmnet subscription ID GUID
- Example: MS-VDI environment SUB ID
- "[HUB_SUBSCRIPTION_ID]"
- Hub environment subscription ID
- This is usually the "Shared Services" environment ID
- "[ARTIFACT_STORAGE_LOCATION]"
- Azure Location of the artifact storage account
- Usually the Azure location of the "Shared Services" environment
- "[DOMAIN_ADMIN_USER_NAME]"
- Domain user name - will be used for AD deployment and not yet included in current deployment
- "[DOMAIN_ADMIN_USER_PASSWORD]"
@ -125,13 +143,19 @@ $ENV:AZURE_SENTINEL = "[BOOLEAN]"
- Ex. $ENV:ADMIN_USER_PWD=""
- "[SSH_KEY]"
- Needs to be a valid public ssh rsa key for SSH to linux box
- "[BOOLEAN]
- "[BOOLEAN]"
- This value needs to be "True" or "False"
- "True" will deploy Azure Sentinel to the Shared Services Environment
- "False" will NOT deploy Azure Sentinel
To use the above script:
1. Return to the PS 7 working directory
2. Make a copy of the above script and replace the necessary values.
3. Copy the script into the clipboard and paste it in the terminal.
4. Verify the environment variables are set by running `Get-ChildItem Env:` to view the current values.
If you are using docker:
1. Return to the running Docker container from earlier in the quickstart.
2. Confirm that you are in the `/usr/src/app` directory.
3. Make a copy of the above script and replace the necessary values.
@ -229,4 +253,5 @@ For safety reasons, the key vault will not be deleted. Instead, it will be set t
``` PowerShell
Get-AzKeyVault -InRemovedState | ? { Write-Host "Removing vault: $($_.VaultName)"; Remove-AzKeyVault -InRemovedState -VaultName $_.VaultName -Location $_.Location -Force }
```
```

Просмотреть файл

@ -0,0 +1,5 @@
{
"Subscriptions": "env(VDC_SUBSCRIPTIONS)",
"Parameters": "file(./parameters.json)",
"Orchestration": "file(./orchestration.json)"
}

Просмотреть файл

@ -0,0 +1,54 @@
{
"ModuleConfigurationsPath": "../../Modules",
"ModuleConfigurations": [
{
"Name": "HostPool",
"ModuleDefinitionName": "WVDHostPool",
"ResourceGroupName": "${Parameters.ModuleConfigurationParameters.HostPool.ResourceGroup}",
"DependsOn": [
],
"Deployment": {
"OverrideParameters": {
"hostPoolName": {
"value": "${Parameters.ModuleConfigurationParameters.HostPool.Name}"
},
"hostPoolType": {
"value": "${Parameters.ModuleConfigurationParameters.HostPool.hostPoolType}"
},
"assignmentType": {
"value": "${Parameters.ModuleConfigurationParameters.HostPool.assignmentType}"
},
"maxSessionLimit": {
"value": "${Parameters.ModuleConfigurationParameters.HostPool.maxSessionLimit}"
},
"loadBalancingAlgorithm": {
"value": "${Parameters.ModuleConfigurationParameters.HostPool.loadBalancingAlgorithm}"
}
}
}
},
{
"Name": "ApplicationGroup",
"ModuleDefinitionName": "WVDApplicationGroup",
"ResourceGroupName": "${Parameters.ModuleConfigurationParameters.HostPool.ResourceGroup}",
"DependsOn": [
"HostPool"
],
"Deployment": {
"OverrideParameters": {
"hostPoolId": {
"value": "reference(HostPool.HostPoolId)"
},
"applicationGroupName": {
"value": "${Parameters.ModuleConfigurationParameters.applicationGroup.name}"
},
"appGroupType": {
"value": "${Parameters.ModuleConfigurationParameters.applicationGroup.appGroupType}"
}
}
}
}
]
}

Просмотреть файл

@ -0,0 +1,59 @@
{
"Organization": "env(ORGANIZATION_NAME)",
"DeploymentName": "wvd",
"InstanceName": "${Parameters.Organization}-${Parameters.DeploymentName}",
"Subscription": "VDCVDI",
"Location": "env(AZURE_LOCATION)",
"StorageBlobUrl": "env(AZURE_STORAGE_BLOB_URL)",
"ModuleConfigurationParameters": {
"SharedServices": {
"DeploymentName": "shrdsvcs",
"ActiveDirectory": {
"VmIpAddressStart": [ "172.0.0.10" ]
},
"VirtualNetworkHUB": {
"Id": "/subscriptions/${Subscriptions.SharedServices.SubscriptionId}/resourceGroups/${Parameters.ModuleConfigurationParameters.SharedServices.VirtualNetworkHUB.ResourceGroupName}/providers/Microsoft.Network/virtualNetworks/${Parameters.ModuleConfigurationParameters.SharedServices.VirtualNetworkHUB.Name}",
"Name": "${Parameters.Organization}-shrdsvcs-vnet",
"ResourceGroupName": "${Parameters.organization}-shrdsvcs-network-rg",
"AddressPrefix": "172.0.0.0/16",
"NetworkVirtualAppliance": {
"AzureFirewall": {
"Name": "${Parameters.Organization}-${Parameters.ModuleConfigurationParameters.SharedServices.DeploymentName}-azfw"
}
}
},
"VirtualNetworkSPOKE": {
"Id": "/subscriptions/${Subscriptions.VDCVDI.SubscriptionId}/resourceGroups/${Parameters.ModuleConfigurationParameters.SharedServices.VirtualNetworkSPOKE.ResourceGroupName}/providers/Microsoft.Network/virtualNetworks/${Parameters.ModuleConfigurationParameters.SharedServices.VirtualNetworkSPOKE.Name}",
"Name": "${Parameters.InstanceName}-SPOKE",
"ResourceGroupName": "${Parameters.InstanceName}-spokenetwork-rg",
"AddressPrefixes": "172.60.0.0/16"
}
},
"OnPremisesInformation": {
"InstanceName": "${Parameters.InstanceName}",
"Comments": "This InstanceName is a temporal value, this value is used in artifactsStorageAccount.json, the idea is to have a global set of services and this name should point to the InstanceName (deployment name) of the global services archetype"
},
"KeyVaultManagementUserId": "env(KEYVAULT_MANAGEMENT_USER_ID)",
"DevOpsServicePrincipalId": "env(DEVOPS_SERVICE_PRINCIPAL_USER_ID)",
"HostPool": {
"Name": "hostPoolTest",
"ResourceGroup": "${Parameters.InstanceName}-wvd-rg",
"Location": "${Parameters.Location}",
"hostPoolType": "Pooled",
"assignmentType": "",
"maxSessionLimit": "20",
"loadBalancingAlgorithm": "breadthfirst"
},
"applicationGroup": {
"Name": "appGroup",
"ResourceGroup": "${Parameters.InstanceName}-wvd-rg",
"Location": "${Parameters.Location}",
"appGroupType": "Desktop"
}
}
}

Просмотреть файл

Просмотреть файл

@ -0,0 +1,198 @@
# **To deploy Azure Virtual Datacenter for Windows Virtual Desktop**
THIS SETUP IS NOT COMPLETE YET. AS of 6/12/20.
### Clone the repository
These steps assume that the `git` command is on your path.
1. Open a terminal window
2. Navigate to a folder where you want to store the source for the toolkit. For, e.g. `c:\git`, navigate to that folder.
3. Run `git clone https://github.com/RKSelvi/vdc.git`. This will clone the GitHub repository in a folder named `vdc`.
4. Run `cd vdc` to change directory in the source folder.
5. Run `git checkout master` to switch to the branch with the current in-development version of the toolkit.
## Configure the toolkit
To configure the toolkit for this quickstart, we will need to collect the following information.
You'll need:
- A subscription for the toolkit to use for logging and tracking deployment.
- The associated tenant id of the Azure Active Directory associated with those subscriptions.
- The object id of the user account that you'll use to run the deployment.
- The object id of a [service principal](https://docs.microsoft.com/azure/active-directory/develop/howto-create-service-principal-portal) that Azure DevOps can use for deployment. This is only for CI/CD pipeline
- An organization name for generating a prefix for naming resources.
- The desired username and password for the Active Directory domain admin that will be created. Active Directory is not deployed now.
- The desired password of the Windows jumpbox.
Note: You can use a single subscription. You'll just need to provide the same subscription id in multiple locations in the configuration.
### Collecting user object id and tentant id
You can get your user object id and tenant id in the portal or by using command line utitilies.
Using Azure PowerShell:
1. Run `Connect-AzAccount -Tenant "[TENANT_ID]" -SubscriptionId "[SUBSCRIPTION_ID]" -EnvironmentName "[AZURE_ENVIRONMENT]"` to login and set an Azure context.
1. Run `Get-AzContext | % { Get-AzADUser -UserPrincipalName $($_.Account.Id) } | select Id` to get the user object id.
1. Run `Get-AzContext | select Tenant` to get the tenant id.
### Setting the configuration
To deploy the MS-VDI environment, you will need to modify two configuration files and set several environmental variables.
#### [`Config\toolkit.subscription.json`](../Config/toolkit.subscription.json)
This file is for toolkit configuration in general.
- Set `Subscription.TenantId` to the tenant id note above.
- Set `Subscription.SubscriptionId` to the id of the subscription used for logging and deployment state tracking noted above.
- Set `Subscription.Location` for Azure regions. For e.g. for Azure Gov, "USGov Virginia"
#### [`Environments\_Common\subscriptions.json`](../Environments/_Common/subscriptions.json)
This file is for the deployment enviroments configuration. Update the subscription & tenant id's in the following environments in the subscriptions.json, `VDCVDI`, `SharedServices` & `Artifacts`
- Set `TenantId` to the tenant id note above.
- Set `SubscriptionId` to the id of the target subscription for the deployment noted above.
### Setting the Environmental variables
The toolkit uses environmental variables instead of configuration files to help avoid the accidental inclusion of secrets into your source control. In the context of a CI/CD pipeline, these values would be retrieved from a key vault.
Set these environmental variables by substituting the actual values in the script below.
Copy and paste this script into PowerShell to execute it.
Note: The first two variables are set with the content of the configuration files we just modified. The path will not resolve correctly unless you are in `/usr/src/app` directory.
```PowerShell
$ENV:VDC_SUBSCRIPTIONS = (Get-Content .\Environments\_Common\subscriptions.json -Raw)
$ENV:VDC_TOOLKIT_SUBSCRIPTION = (Get-Content .\Config\toolkit.subscription.json -Raw)
$ENV:ORGANIZATION_NAME = "[ORGANIZATION_NAME]"
$ENV:AZURE_ENVIRONMENT_NAME = "[AZURE_ENVIRONMENT]"
$ENV:AZURE_LOCATION = "[AZURE_REGION]"
$ENV:TENANT_ID = "[TENANT_ID]"
$ENV:SUBSCRIPTION_ID = "[SUBSCRIPTION_ID]"
$ENV:KEYVAULT_MANAGEMENT_USER_ID = "[KEY_VAULT_MANAGEMENT_USER_ID]"
$ENV:DEVOPS_SERVICE_PRINCIPAL_USER_ID = "[SERVICE_PRINCIPAL_USER_ID]"
$ENV:DOMAIN_ADMIN_USERNAME = "[DOMAIN_ADMIN_USER_NAME]"
$ENV:DOMAIN_ADMIN_USER_PWD = "[DOMAIN_ADMIN_USER_PASSWORD]"
$ENV:ADMIN_USER_NAME = "[VM_ADMIN_USER_NAME]"
$ENV:ADMIN_USER_PWD = "[VM_ADMIN_USER_PASSWORD]"
$ENV:AZURE_DISCOVERY_URL = "https://management.azure.com/metadata/endpoints?api-version=2019-05-01"
```
**NOTE:** Examples to setting the env variables
- "[ORGANIZATION_NAME]"
- Abbreviation of your org (for e.g. contoso) with **NO SPACES**
- "[AZURE_ENVIRONMENT]"
- For Azure Commercial
- "AzureCloud"
- For Azure Government
- "AzureUSGovernment"
- "[AZURE_REGION]" - Depending on the Azure Enviroment, provide Azure regions. For e.g.
- Azure public cloud
- "East US"
- "East US 2"
- Azure Government
- "USGov Virginia"
- "USGov Iowa"
- "[KEY_VAULT_MANAGEMENT_USER_ID]"
- User's GUID from AAD deploying the VDC toolkit
- "[SERVICE_PRINCIPAL_USER_ID]"
- Used by CI/CD (not yet implemented). Can be same as KEY_VAULT_MANAGEMENT_USER_ID
- "[TENANT_ID]" - Tenant's GUID
- "[SUBSCRIPTION_ID]" - Subscription's GUID
- "[DOMAIN_ADMIN_USER_NAME]"
- Domain user name - will be used for AD deployment and not yet included in current deployment
- "[DOMAIN_ADMIN_USER_PASSWORD]"
- Domain user password - will be used for AD deployment and not yet included in current deployment. Follow the [guidelines](https://docs.microsoft.com/en-us/azure/virtual-machines/windows/faq#what-are-the-password-requirements-when-creating-a-vm) for setting the password.
- "[VM_ADMIN_USER_NAME]"
- VM log in username
- "[VM_ADMIN_USER_PASSWORD]"
- VM user password. Follow the [guidelines](https://docs.microsoft.com/en-us/azure/virtual-machines/windows/faq#what-are-the-password-requirements-when-creating-a-vm) for setting the password.
To set the environment values:
1. Return to the running Docker container from earlier in the quickstart.
2. Confirm that you are in the `/usr/src/app` directory.
3. Make a copy of the above script and replace the necessary values.
4. Copy the script into the clipboard and paste it in the terminal.
5. Verify that the enviromental variables are set by running `env` to view the current values.
### Setting the Parameters
Any application specific parameters updates should be done in the [parameters.json](../../Environments/MS-VDI/parameters.json) file such as IP address, subnet names, subnet range, secrets etc.
## Deploying the MS-VDI environment
1. Return to the running Docker container from earlier in the quickstart.
1. If you have not already done so, run `Connect-AzAccount -Tenant "[TENANT_ID]" -SubscriptionId "[SUBSCRIPTION_ID]" -EnvironmentName "[AZURE_ENVIRONMENT]"` to login and set an Azure context.
1. To deploy the entire MS-VDI environment, you can run a single command:
``` PowerShell
./Orchestration/OrchestrationService/ModuleConfigurationDeployment.ps1 -DefinitionPath ./Environments/MS-VDI/definition.json
```
The toolkit will begin deploying the constituent modules and the status will be sent to the terminal.
Open the [Azure portal](https://portal.azure.us) and you can check the status of the invididual deployments. Azure portal link will be based on azure environment.
## Deploying individual modules
If you prefer you can deploy the constituent modules for MS-VDI individually.
The following is the series of commands to execute.
``` PowerShell
.\Orchestration\OrchestrationService\ModuleConfigurationDeployment.ps1 -DefinitionPath .\Environments\MS-VDI\definition.json -ModuleConfigurationName "VirtualNetworkHUB"
.\Orchestration\OrchestrationService\ModuleConfigurationDeployment.ps1 -DefinitionPath .\Environments\MS-VDI\definition.json -ModuleConfigurationName "VirtualNetworkSPOKE"
.\Orchestration\OrchestrationService\ModuleConfigurationDeployment.ps1 -DefinitionPath .\Environments\MS-VDI\definition.json -ModuleConfigurationName "VirtualNetworkPeeringHub"
.\Orchestration\OrchestrationService\ModuleConfigurationDeployment.ps1 -DefinitionPath .\Environments\MS-VDI\definition.json -ModuleConfigurationName "VirtualNetworkPeeringSpoke"
.\Orchestration\OrchestrationService\ModuleConfigurationDeployment.ps1 -DefinitionPath .\Environments\MS-VDI\definition.json -ModuleConfigurationName "DiagnosticStorageAccount"
.\Orchestration\OrchestrationService\ModuleConfigurationDeployment.ps1 -DefinitionPath .\Environments\MS-VDI\definition.json -ModuleConfigurationName "EnableServiceEndpointOnDiagnosticStorageAccount"
.\Orchestration\OrchestrationService\ModuleConfigurationDeployment.ps1 -DefinitionPath .\Environments\MS-VDI\definition.json -ModuleConfigurationName "LogAnalytics"
.\Orchestration\OrchestrationService\ModuleConfigurationDeployment.ps1 -DefinitionPath .\Environments\MS-VDI\definition.json -ModuleConfigurationName "KeyVault"
.\Orchestration\OrchestrationService\ModuleConfigurationDeployment.ps1 -DefinitionPath .\Environments\MS-VDI\definition.json -ModuleConfigurationName "ArtifactsStorageAccount"
.\Orchestration\OrchestrationService\ModuleConfigurationDeployment.ps1 -DefinitionPath .\Environments\MS-VDI\definition.json -ModuleConfigurationName "UploadScriptsToArtifactsStorage"
.\Orchestration\OrchestrationService\ModuleConfigurationDeployment.ps1 -DefinitionPath .\Environments\MS-VDI\definition.json -ModuleConfigurationName "JumpboxASG"
.\Orchestration\OrchestrationService\ModuleConfigurationDeployment.ps1 -DefinitionPath .\Environments\MS-VDI\definition.json -ModuleConfigurationName "WindowsVM"
```
**NOTE:**
1. If deployment reports, unable to find deployment storage account, it could be that PowerShell is not connected to Azure.
2. Open a new PowerShell/Docker instance if there was any changes to files in Environments folder
### **Teardown the environment**
``` PowerShell
./Orchestration/OrchestrationService/ModuleConfigurationDeployment.ps1 -TearDownEnvironment -DefinitionPath ./Environments/MS-VDI/definition.json
```
Note: This is the same command you used to deploy except that you include ` -TearDownEnvironment`.
It uses the same configuration, so if you change the configuration the tear down may not execute as expected.
### **Remove vdc-toolkit-rg**
Teardown removes only the resources deployed from VDC toolkit orchestration but do not actually remove the resource group (vdc-toolkit-rg) and storage accounts created by VDC toolkit deployment.
vdc-toolkit-rg
Use the Azure Cli to remove the resource group and the storage accounts. Find the storage account name from the vdc-toolkit-rg resource group.
``` AzureCli
az account set --subscription [SUBSCRIPTION_ID]
az storage container legal-hold clear --resource-group vdc-toolkit-rg --account-name [STORAGE_ACCOUNT_NAME] --container-name deployments --tags audit
az storage container legal-hold clear --resource-group vdc-toolkit-rg --account-name [STORAGE_ACCOUNT_NAME] --container-name audit --tags audit
```
### **Remove KeyVault**
For safety reasons, the key vault will not be deleted. Instead, it will be set to a _removed_ state. This means that the name is still considered in use. To fully delete the key vault, use:
``` PowerShell
Get-AzKeyVault -InRemovedState | ? { Write-Host "Removing vault: $($_.VaultName)";
Remove-AzKeyVault -InRemovedState -VaultName $_.VaultName -Location $_.Location -Force }
```

Просмотреть файл

@ -2,7 +2,7 @@
"Name": "${Parameters.Organization}cstmartfcts11",
"ResourceGroup": "${Parameters.Organization}-artifacts-rg",
"Sku": "Standard_GRS",
"Location": "${Parameters.Location}",
"Location": "env(ARTIFACT_LOCATION)",
"InstanceName": "${Parameters.ModuleConfigurationParameters.OnPremisesInformation.InstanceName}",
"Policies": {
"Effect": "Audit"

Просмотреть файл

@ -15,6 +15,11 @@
"TenantId": "000000-000-0000-0000",
"SubscriptionId": "000000-000-0000-0000"
},
"SPOKE": {
"Comments": "Shared services subscription and tenant information",
"TenantId": "000000-000-0000-0000",
"SubscriptionId": "000000-000-0000-0000"
},
"AKS": {
"Comments": "Shared services subscription and tenant information",
"TenantId": "000000-000-0000-0000",

55
Environments/readme.md Normal file
Просмотреть файл

@ -0,0 +1,55 @@
## Environment Overview
Environments are the heart of the VDC toolkit. Each environment is a set of parameter and orchestration files that deploy [modules](../Modules).
The modules consist of full deployments for different Azure resources. Ex. Virtual Machines, Virtual Networks, Key Vault etc.
- Modules can have RBAC policies
- Azure policies
- Configuration scripts
- Tests to ensure the module is working as expected
Think of the VDC toolkit as a nested ARM template deployment.
You can specify any module you would like to deploy in the Orchestration.json file under each environemnt.
Then add parameters for the module under the parameters.json file under the specificed enviornment you are deploying.
If you want to **disable or "not deploy"** certain resources in the orchestration file without deleting them. Please refer to [folder replication documentation](../Docs/masterOrchestration/folder_replication.md).
If you are adding additional resources to the environment orchestration file. Consider the following example.
### Storage Account example
You wish to add an additional storage account to the [MS-VDI environment](../Environments/MS-VDI).
For this example we would utilize the [Storage Account Module](../Modules/StorageAccounts).
This module has a `deploy.json` file which deploys the storage account ARM template.
If we look at the [deploy.json](../Modules/StorageAccounts/deploy.json) file we can see that it has several parameters needed.
1. "storageAccountName"
- Required
2. "location"
- Default is the resource group location
3. "storageAccountKind"
- Default is StorageV2
ETC.
#### Parameters.json
You can either accept the default values OR in the parameters.json file under [MS-VDI parameter.json](../Environments/MS-VDI/parameters.json)
specify the values you wish to have for the storage account. Example below Picture 1.
![st](../images/storage_account_ex1.png)
*Picture 1*
#### Orchestration.json
Then in the [Orchestration.json](../Environments/MS-VDI/orchestration.json) file configure it with references to the parameters.json file. You
can also override the parameters.
You must specify the "ModuleDefinitionName" (Yellow arrow in Picture 2) - This must match the module folder name exactly.
The [`Deployment`](../Modules/StorageAccounts/deploy.json) section will be where you reference the parameters for the *deploy.json* file in the modules section
- (2nd Red arrow in Picture 2)
The [`Policies`](../Modules/StorageAccounts/Policy) section is where you would set an Azure Policy for storage accounts.
- (1st red arrow in Picture 2)
If [`RBAC`](../Modules/StorageAccounts/RBAC) roles needed to be added you would add this section as well.
![](../images/storage_account_ex2.png)

Просмотреть файл

Просмотреть файл

Просмотреть файл

Просмотреть файл

Просмотреть файл

@ -0,0 +1,168 @@
<#
.NOTES
==============================================================================================
Copyright(c) Microsoft Corporation. All rights reserved.
File: module.tests.ps1
Purpose: Pester - Test vNet Peering Templates
Version: 1.0.0.0 - 1st April 2019 - Azure Virtual Datacenter Development Team
==============================================================================================
.SYNOPSIS
This script contains functionality used to test vNet Peering ARM template syntax.
.DESCRIPTION
This script contains functionality used to test vNet Peering ARM template syntax.
Deployment steps of the script are outlined below.
1) Test Template File Syntax
2) Test Parameter File Syntax
3) Test Template and Parameter File Compactibility
#>
#Requires -Version 5
#region - Parameters
$here = Split-Path -Parent $MyInvocation.MyCommand.Path
$here = Join-Path $here ".."
$template = Split-Path -Leaf $here
$TemplateFileTestCases = @()
ForEach ( $File in (Get-ChildItem (Join-Path "$here" "deploy.json") -Recurse | Select-Object -ExpandProperty Name) ) {
$TemplateFileTestCases += @{ TemplateFile = $File }
}
$ParameterFileTestCases = @()
ForEach ( $File in (Get-ChildItem (Join-Path "$here" "Tests" -AdditionalChildPath @("*parameters.json")) -Recurse -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Name) ) {
$ParameterFileTestCases += @{ ParameterFile = Join-Path "Tests" $File }
}
$Modules = @();
ForEach ( $File in (Get-ChildItem (Join-Path "$here" "deploy.json") ) ) {
$Module = [PSCustomObject]@{
'Template' = $null
'Parameters' = $null
}
$Module.Template = $File.FullName;
$Parameters = @();
ForEach ( $ParameterFile in (Get-ChildItem (Join-Path "$here" "Tests" -AdditionalChildPath @("*parameters.json")) -Recurse -ErrorAction SilentlyContinue| Select-Object -ExpandProperty Name) ) {
$Parameters += (Join-Path "$here" "Tests" -AdditionalChildPath @("$ParameterFile") )
}
$Module.Parameters = $Parameters;
$Modules += @{ Module = $Module };
}
#endregion
#region - Run Pester Test Script
Describe "Template: $template - vNetPeering" -Tags Unit {
Context "Template File Syntax" {
It "Has a JSON template file" {
(Join-Path "$here" "*deploy.json") | Should Exist
}
It "Converts from JSON and has the expected properties" -TestCases $TemplateFileTestCases {
Param ($TemplateFile)
$expectedProperties = '$schema',
'contentVersion',
'parameters',
'variables',
'resources',
'outputs'| Sort-Object
$templateProperties = (Get-Content (Join-Path "$here" "$TemplateFile") `
| ConvertFrom-Json -ErrorAction SilentlyContinue) `
| Get-Member -MemberType NoteProperty `
| Sort-Object -Property Name `
| ForEach-Object Name
$templateProperties | Should Be $expectedProperties
}
}
Context "Parameter File Syntax" {
It "Parameter file does not contains the expected properties" -TestCases $ParameterFileTestCases {
Param( $ParameterFile )
$expectedProperties = '$schema',
'contentVersion',
'parameters' | Sort-Object
Write-Host $ParameterFile
Join-Path "$here" "$ParameterFile" | Write-Host
$templateFileProperties = (Get-Content (Join-Path "$here" "$ParameterFile") `
| ConvertFrom-Json -ErrorAction SilentlyContinue) `
| Get-Member -MemberType NoteProperty `
| Sort-Object -Property Name `
| ForEach-Object Name
$templateFileProperties | Should Be $expectedProperties
}
}
Context "Template and Parameter Compactibility" {
It "Is count of required parameters in template file equal or lesser than count of all parameters in parameters file" -TestCases $Modules {
Param( $Module )
$requiredParametersInTemplateFile = (Get-Content "$($Module.Template)" `
| ConvertFrom-Json -ErrorAction SilentlyContinue).Parameters.PSObject.Properties `
| Where-Object -FilterScript { -not ($_.Value.PSObject.Properties.Name -eq "defaultValue") } `
| Sort-Object -Property Name `
| ForEach-Object Name
ForEach ( $Parameter in $Module.Parameters ) {
$allParametersInParametersFile = (Get-Content $Parameter `
| ConvertFrom-Json -ErrorAction SilentlyContinue).Parameters.PSObject.Properties `
| Sort-Object -Property Name `
| ForEach-Object Name
if ($requiredParametersInTemplateFile.Count -gt $allParametersInParametersFile.Count) {
Write-Host "Mismatch found, parameters from parameter file are more than the expected in the template"
Write-Host "Required parameters are: $(ConvertTo-Json $requiredParametersInTemplateFile)"
Write-Host "Parameters from parameter file are: $(ConvertTo-Json $allParametersInParametersFile)"
}
$requiredParametersInTemplateFile.Count | Should Not BeGreaterThan $allParametersInParametersFile.Count;
}
}
It "Has all parameters in parameters file existing in template file" -TestCases $Modules {
Param( $Module )
$allParametersInTemplateFile = (Get-Content "$($Module.Template)" `
| ConvertFrom-Json -ErrorAction SilentlyContinue).Parameters.PSObject.Properties `
| Sort-Object -Property Name `
| ForEach-Object Name
ForEach ( $Parameter in $Module.Parameters ) {
Write-Host "File analyzed: $Parameter";
$allParametersInParametersFile = (Get-Content $Parameter `
| ConvertFrom-Json -ErrorAction SilentlyContinue).Parameters.PSObject.Properties `
| Sort-Object -Property Name `
| ForEach-Object Name
$result = @($allParametersInParametersFile| Where-Object {$allParametersInTemplateFile -notcontains $_});
Write-Host "Invalid parameters: $(ConvertTo-Json $result)";
@($allParametersInParametersFile| Where-Object {$allParametersInTemplateFile -notcontains $_}).Count | Should Be 0;
}
}
It "Has required parameters in template file existing in parameters file" -TestCases $Modules {
Param( $Module )
$requiredParametersInTemplateFile = (Get-Content "$($Module.Template)" `
| ConvertFrom-Json -ErrorAction SilentlyContinue).Parameters.PSObject.Properties `
| Where-Object -FilterScript { -not ($_.Value.PSObject.Properties.Name -eq "defaultValue") } `
| Sort-Object -Property Name `
| ForEach-Object Name
ForEach ( $Parameter in $Module.Parameters ) {
$allParametersInParametersFile = (Get-Content $Parameter `
| ConvertFrom-Json -ErrorAction SilentlyContinue).Parameters.PSObject.Properties `
| Sort-Object -Property Name `
| ForEach-Object Name
$invalid = $requiredParametersInTemplateFile | Where-Object {$allParametersInParametersFile -notcontains $_}
if ($invalid.Count -gt 0) {
Write-Host "Invalid parameters: $(ConvertTo-Json $invalid)"
}
@($requiredParametersInTemplateFile | Where-Object {$allParametersInParametersFile -notcontains $_}).Count | Should Be 0;
}
}
}
}
#endregion

Просмотреть файл

@ -0,0 +1,15 @@
{
"$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"localVnetName": {
"value": "shared-vdc-vnet"
},
"peeringName": {
"value": "appPeering"
},
"remoteVirtualNetworkId": {
"value": "/subscriptions/{subscription-id}/resourceGroups/{resource-group}/providers/Microsoft.Network/virtualNetworks/{nvet-name}"
}
}
}

Просмотреть файл

@ -0,0 +1,163 @@
<#
.NOTES
==============================================================================================
Copyright(c) Microsoft Corporation. All rights reserved.
File: module.tests.ps1
Purpose: Pester - Test Virtual Network Peering ARM Templates
Version: 1.0.0.0 - 1st April 2019 - Azure Virtual Datacenter Development Team
==============================================================================================
.SYNOPSIS
This script contains functionality used to test Azure Virtual Network Peering ARM template synatax.
.DESCRIPTION
This script contains functionality used to test Azure Virtual Network Peering ARM template synatax.
Deployment steps of the script are outlined below.
1) Test Template File Syntax
2) Test Parameter File Syntax
3) Test Template and Parameter File Compactibility
#>
#Requires -Version 5
#region Parameters
$here = Split-Path -Parent $MyInvocation.MyCommand.Path
$here = Join-Path $here ".."
$template = Split-Path -Leaf $here
$TemplateFileTestCases = @()
ForEach ( $File in (Get-ChildItem (Join-Path "$here" "Policy" -AdditionalChildPath @("deploy.json")) -Recurse -ErrorAction SilentlyContinue| Select-Object -ExpandProperty Name) ) {
$TemplateFileTestCases += @{ TemplateFile = $File }
}
$ParameterFileTestCases = @()
ForEach ( $File in (Get-ChildItem (Join-Path "$here" "Policy" -AdditionalChildPath @("*parameters.json")) -Recurse -ErrorAction SilentlyContinue| Select-Object -ExpandProperty Name) ) {
$ParameterFileTestCases += @{ ParameterFile = Join-Path "Policy" $File }
}
$Modules = @();
ForEach ( $File in (Get-ChildItem (Join-Path "$here" "Policy" -AdditionalChildPath @("deploy.json")) -ErrorAction SilentlyContinue ) ) {
$Module = [PSCustomObject]@{
'Template' = $null
'Parameters' = $null
}
$Module.Template = $File.FullName;
$Parameters = @();
ForEach ( $ParameterFile in (Get-ChildItem (Join-Path "$here" "Policy" -AdditionalChildPath @("*parameters.json")) -Recurse -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Name) ) {
$Parameters += (Join-Path "$here" "Policy" -AdditionalChildPath @("$ParameterFile") )
}
$Module.Parameters = $Parameters;
$Modules += @{ Module = $Module };
}
#endregion
if ($null -ne $TemplateFileTestCases -and
$TemplateFileTestCases.Count -gt 0) {
#region Run Pester Test Script
Describe "Template: $template - Virtual Network Peering" -Tags Unit {
Context "Template File Syntax" {
It "Has a JSON template file" {
(Join-Path "$here" "deploy.json") | Should Exist
}
It "Converts from JSON and has the expected properties" -TestCases $TemplateFileTestCases {
Param( $TemplateFile )
$expectedProperties = '$schema',
'contentVersion',
'parameters',
'variables',
'resources',
'outputs' | Sort-Object
$templateProperties = (Get-Content (Join-Path "$here" "$TemplateFile") `
| ConvertFrom-Json -ErrorAction SilentlyContinue) `
| Get-Member -MemberType NoteProperty `
| Sort-Object -Property Name `
| ForEach-Object Name
$templateProperties | Should Be $expectedProperties
}
}
Context "Parameter File Syntax" {
It "Parameter file does not contains the expected properties" -TestCases $ParameterFileTestCases {
Param( $ParameterFile )
$expectedProperties = '$schema',
'contentVersion',
'parameters' | Sort-Object
Write-Host $ParameterFile
Join-Path "$here" "$ParameterFile" | Write-Host
$templateFileProperties = (Get-Content (Join-Path "$here" "$ParameterFile") `
| ConvertFrom-Json -ErrorAction SilentlyContinue) `
| Get-Member -MemberType NoteProperty `
| Sort-Object -Property Name `
| ForEach-Object Name
$templateFileProperties | Should Be $expectedProperties
}
}
Context "Template and Parameter Compactibility" {
It "Is count of required parameters in template file equal or lesser than count of all parameters in parameters file" -TestCases $Modules {
Param( $Module )
$requiredParametersInTemplateFile = (Get-Content "$($Module.Template)" `
| ConvertFrom-Json -ErrorAction SilentlyContinue).Parameters.PSObject.Properties `
| Where-Object -FilterScript { -not ($_.Value.PSObject.Properties.Name -eq "defaultValue") } `
| Sort-Object -Property Name `
| ForEach-Object Name
ForEach ( $Parameter in $Module.Parameters ) {
$allParametersInParametersFile = (Get-Content $Parameter `
| ConvertFrom-Json -ErrorAction SilentlyContinue).Parameters.PSObject.Properties `
| Sort-Object -Property Name `
| ForEach-Object Name
$requiredParametersInTemplateFile.Count | Should Not BeGreaterThan $allParametersInParametersFile.Count;
}
}
It "Has all parameters in parameters file existing in template file" -TestCases $Modules {
Param( $Module )
$allParametersInTemplateFile = (Get-Content "$($Module.Template)" `
| ConvertFrom-Json -ErrorAction SilentlyContinue).Parameters.PSObject.Properties `
| Sort-Object -Property Name `
| ForEach-Object Name
ForEach ( $Parameter in $Module.Parameters ) {
Write-Host "File analyzed: $Parameter";
$allParametersInParametersFile = (Get-Content $Parameter `
| ConvertFrom-Json -ErrorAction SilentlyContinue).Parameters.PSObject.Properties `
| Sort-Object -Property Name `
| ForEach-Object Name
$result = @($allParametersInParametersFile| Where-Object {$allParametersInTemplateFile -notcontains $_});
Write-Host "Invalid parameters: $(ConvertTo-Json $result)";
@($allParametersInParametersFile| Where-Object {$allParametersInTemplateFile -notcontains $_}).Count | Should Be 0;
}
}
It "Has required parameters in template file existing in parameters file" -TestCases $Modules {
Param( $Module )
$requiredParametersInTemplateFile = (Get-Content "$($Module.Template)" `
| ConvertFrom-Json -ErrorAction SilentlyContinue).Parameters.PSObject.Properties `
| Where-Object -FilterScript { -not ($_.Value.PSObject.Properties.Name -eq "defaultValue") } `
| Sort-Object -Property Name `
| ForEach-Object Name
ForEach ( $Parameter in $Module.Parameters ) {
$allParametersInParametersFile = (Get-Content $Parameter `
| ConvertFrom-Json -ErrorAction SilentlyContinue).Parameters.PSObject.Properties `
| Sort-Object -Property Name `
| ForEach-Object Name
@($requiredParametersInTemplateFile| Where-Object {$allParametersInParametersFile -notcontains $_}).Count | Should Be 0;
}
}
}
}
#endregion
}

Просмотреть файл

@ -0,0 +1,163 @@
<#
.NOTES
==============================================================================================
Copyright(c) Microsoft Corporation. All rights reserved.
File: module.tests.ps1
Purpose: Pester - Test Virtual Network Peering ARM Templates
Version: 1.0.0.0 - 1st April 2019 - Azure Virtual Datacenter Development Team
==============================================================================================
.SYNOPSIS
This script contains functionality used to test Azure Virtual Network Peering ARM template synatax.
.DESCRIPTION
This script contains functionality used to test Azure Virtual Network Peering ARM template synatax.
Deployment steps of the script are outlined below.
1) Test Template File Syntax
2) Test Parameter File Syntax
3) Test Template and Parameter File Compactibility
#>
#Requires -Version 5
#region Parameters
$here = Split-Path -Parent $MyInvocation.MyCommand.Path
$here = Join-Path $here ".."
$template = Split-Path -Leaf $here
$TemplateFileTestCases = @()
ForEach ( $File in (Get-ChildItem (Join-Path "$here" "RBAC" -AdditionalChildPath @("deploy.json")) -ErrorAction SilentlyContinue -Recurse | Select-Object -ExpandProperty Name) ) {
$TemplateFileTestCases += @{ TemplateFile = $File }
}
$ParameterFileTestCases = @()
ForEach ( $File in (Get-ChildItem (Join-Path "$here" "RBAC" -AdditionalChildPath @("*parameters.json")) -ErrorAction SilentlyContinue -Recurse | Select-Object -ExpandProperty Name) ) {
$ParameterFileTestCases += @{ ParameterFile = Join-Path "RBAC" $File }
}
$Modules = @();
ForEach ( $File in (Get-ChildItem (Join-Path "$here" "RBAC" -AdditionalChildPath @("deploy.json")) -ErrorAction SilentlyContinue ) ) {
$Module = [PSCustomObject]@{
'Template' = $null
'Parameters' = $null
}
$Module.Template = $File.FullName;
$Parameters = @();
ForEach ( $ParameterFile in (Get-ChildItem (Join-Path "$here" "RBAC" -AdditionalChildPath @("*parameters.json")) -Recurse | Select-Object -ExpandProperty Name) ) {
$Parameters += (Join-Path "$here" "RBAC" -AdditionalChildPath @("$ParameterFile") )
}
$Module.Parameters = $Parameters;
$Modules += @{ Module = $Module };
}
#endregion
if ($null -ne $TemplateFileTestCases -and
$TemplateFileTestCases.Count -gt 0) {
#region Run Pester Test Script
Describe "Template: $template - Virtual Network Peering" -Tags Unit {
Context "Template File Syntax" {
It "Has a JSON template file" -TestCases $TemplateFileTestCases {
(Join-Path "$here" "deploy.json") | Should Exist
}
It "Converts from JSON and has the expected properties" -TestCases $TemplateFileTestCases {
Param( $TemplateFile )
Write-Host "TF: $TemplateFile"
$expectedProperties = '$schema',
'contentVersion',
'parameters',
'variables',
'resources',
'outputs' | Sort-Object
$templateProperties = (Get-Content (Join-Path "$here" "$TemplateFile") `
| ConvertFrom-Json -ErrorAction SilentlyContinue) `
| Get-Member -MemberType NoteProperty `
| Sort-Object -Property Name `
| ForEach-Object Name
$templateProperties | Should Be $expectedProperties
}
}
Context "Parameter File Syntax" {
It "Parameter file does not contains the expected properties" -TestCases $ParameterFileTestCases {
Param( $ParameterFile )
$expectedProperties = '$schema',
'contentVersion',
'parameters' | Sort-Object
Write-Host $ParameterFile
Join-Path "$here" "$ParameterFile" | Write-Host
$templateFileProperties = (Get-Content (Join-Path "$here" "$ParameterFile") `
| ConvertFrom-Json -ErrorAction SilentlyContinue) `
| Get-Member -MemberType NoteProperty `
| Sort-Object -Property Name `
| ForEach-Object Name
$templateFileProperties | Should Be $expectedProperties
}
}
Context "Template and Parameter Compactibility" {
It "Is count of required parameters in template file equal or lesser than count of all parameters in parameters file" -TestCases $Modules {
Param( $Module )
$requiredParametersInTemplateFile = (Get-Content "$($Module.Template)" `
| ConvertFrom-Json -ErrorAction SilentlyContinue).Parameters.PSObject.Properties `
| Where-Object -FilterScript { -not ($_.Value.PSObject.Properties.Name -eq "defaultValue") } `
| Sort-Object -Property Name `
| ForEach-Object Name
ForEach ( $Parameter in $Module.Parameters ) {
$allParametersInParametersFile = (Get-Content $Parameter `
| ConvertFrom-Json -ErrorAction SilentlyContinue).Parameters.PSObject.Properties `
| Sort-Object -Property Name `
| ForEach-Object Name
$requiredParametersInTemplateFile.Count | Should Not BeGreaterThan $allParametersInParametersFile.Count;
}
}
It "Has all parameters in parameters file existing in template file" -TestCases $Modules {
Param( $Module )
$allParametersInTemplateFile = (Get-Content "$($Module.Template)" `
| ConvertFrom-Json -ErrorAction SilentlyContinue).Parameters.PSObject.Properties `
| Sort-Object -Property Name `
| ForEach-Object Name
ForEach ( $Parameter in $Module.Parameters ) {
Write-Host "File analyzed: $Parameter";
$allParametersInParametersFile = (Get-Content $Parameter `
| ConvertFrom-Json -ErrorAction SilentlyContinue).Parameters.PSObject.Properties `
| Sort-Object -Property Name `
| ForEach-Object Name
$result = @($allParametersInParametersFile| Where-Object {$allParametersInTemplateFile -notcontains $_});
Write-Host "Invalid parameters: $(ConvertTo-Json $result)";
@($allParametersInParametersFile| Where-Object {$allParametersInTemplateFile -notcontains $_}).Count | Should Be 0;
}
}
It "Has required parameters in template file existing in parameters file" -TestCases $Modules {
Param( $Module )
$requiredParametersInTemplateFile = (Get-Content "$($Module.Template)" `
| ConvertFrom-Json -ErrorAction SilentlyContinue).Parameters.PSObject.Properties `
| Where-Object -FilterScript { -not ($_.Value.PSObject.Properties.Name -eq "defaultValue") } `
| Sort-Object -Property Name `
| ForEach-Object Name
ForEach ( $Parameter in $Module.Parameters ) {
$allParametersInParametersFile = (Get-Content $Parameter `
| ConvertFrom-Json -ErrorAction SilentlyContinue).Parameters.PSObject.Properties `
| Sort-Object -Property Name `
| ForEach-Object Name
@($requiredParametersInTemplateFile| Where-Object {$allParametersInParametersFile -notcontains $_}).Count | Should Be 0;
}
}
}
}
#endregion
}

Просмотреть файл

@ -0,0 +1,122 @@
{
"$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json",
"contentVersion": "1.0.0.0",
"parameters": {
"localVnetName": {
"type": "string"
},
"RemoteVnetName": {
"type": "string"
},
"localSubscriptionID": {
"type": "string"
},
"remoteSubscriptionID": {
"type": "string"
},
"localResourceGroup": {
"type": "string"
},
"remoteResourceGroup": {
"type": "string"
},
"allowVirtualNetworkAccess": {
"type": "bool",
"defaultValue": true,
"metadata": {
"description": "Optional. Whether the VMs in the local virtual network space would be able to access the VMs in remote virtual network space."
}
},
"allowForwardedTraffic": {
"type": "bool",
"defaultValue": true,
"metadata": {
"description": "Optional. Whether the forwarded traffic from the VMs in the local virtual network will be allowed/disallowed in remote virtual network."
}
},
"allowGatewayTransit": {
"type": "bool",
"defaultValue": false,
"metadata": {
"description": "Optional. If gateway links can be used in remote virtual networking to link to this virtual network."
}
},
"useRemoteGateways": {
"type": "bool",
"defaultValue": true,
"metadata": {
"description": "Optional. If remote gateways can be used on this virtual network. If the flag is set to true, and allowGatewayTransit on remote peering is also true, virtual network will use gateways of remote virtual network for transit. Only one peering can have this flag set to true. This flag cannot be set if virtual network already has a gateway."
}
}
},
"variables": {},
"resources": [
{
"apiVersion": "2017-05-10",
"name": "[concat('Peer-',parameters('localVnetName'), '-to-', parameters('remoteVnetName'))]",
"type": "Microsoft.Resources/deployments",
"subscriptionId": "[parameters('localSubscriptionId')]",
"resourceGroup": "[parameters('localResourceGroup')]",
"properties": {
"mode": "Incremental",
"template": {
"$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json",
"contentVersion": "1.0.0.0",
"variables": {},
"resources": [
{
"apiVersion": "2017-10-01",
"type": "Microsoft.Network/virtualNetworks/virtualNetworkPeerings",
"name": "[concat(parameters('localVnetName'), '/Peered-to-', parameters('remoteVnetName'))]",
"location": "[parameters('localResourceGroup')]",
"properties": {
"allowVirtualNetworkAccess": "[parameters('allowVirtualNetworkAccess')]",
"allowForwardedTraffic": "[parameters('allowForwardedTraffic')]",
"allowGatewayTransit": "[parameters('allowGatewayTransit')]",
"useRemoteGateways": "[parameters('useRemoteGateways')]",
"remoteVirtualNetwork": {
"id": "[resourceId(parameters('remoteSubscriptionID'), parameters('remoteResourceGroup'),'Microsoft.Network/virtualNetworks', parameters ('remoteVnetName'))]"
}
}
}
]
}
}
},
{
"apiVersion": "2017-05-10",
"name": "[concat('Peer-',parameters('remoteVnetName'), '-to-', parameters('localVnetName'))]",
"type": "Microsoft.Resources/deployments",
"resourceGroup": "[parameters('remoteResourceGroup')]",
"subscriptionId": "[parameters('remoteSubscriptionId')]",
"properties": {
"mode": "Incremental",
"template": {
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {},
"variables": {},
"resources": [
{
"apiVersion": "2017-10-01",
"type": "Microsoft.Network/virtualNetworks/virtualNetworkPeerings",
"name": "[concat(parameters('remoteVnetName'), '/Peered-to-', parameters('localVnetName'))]",
"location": "[parameters('remoteResourceGroup')]",
"properties": {
"allowVirtualNetworkAccess": true,
"allowForwardedTraffic": false,
"allowGatewayTransit": false,
"useRemoteGateways": false,
"remoteVirtualNetwork": {
"id": "[resourceId(parameters('localSubscriptionId'), parameters('localResourceGroup'),'Microsoft.Network/virtualNetworks', parameters ('localvnetName'))]"
}
}
}
]
}
}
}
],
"outputs": {
}
}

Просмотреть файл

@ -0,0 +1,35 @@
# vNetPeering
This template deploys Virtual Network Peering.
## Resources
- Microsoft.Network/virtualNetworks/virtualNetworkPeerings
## Parameters
| Parameter Name | Default Value | Description |
| :- | :- | :- |
| `localVnetName` | | Required. The Name of the Virtual Network to add the peering to.
| `peeringName` | | Require. The Name of the virtual network peering resource.
| `remoteVirtualNetworkId` | | Required. The Resource Id of the remote virtual network. The remove virtual network can be in the same or different region.
| `allowVirtualNetworkAccess` | `true` | Optional. Whether the VMs in the local virtual network space would be able to access the VMs in remote virtual network space.
| `allowForwardedTraffic` | | Optional. Whether the forwarded traffic from the VMs in the local virtual network will be allowed/disallowed in remote virtual network.
| `allowGatewayTransit` | | Optional. If gateway links can be used in remote virtual networking to link to this virtual network.
| `useRemoteGateways` | | Optional. If remote gateways can be used on this virtual network. If the flag is set to true, and allowGatewayTransit on remote peering is also true, virtual network will use gateways of remote virtual network for transit. Only one peering can have this flag set to true. This flag cannot be set if virtual network already has a gateway.
## Outputs
| Output Name | Description |
| :- | :- |
| `vNetPeeringResourceResourceGroup` | The Resource Group the vNet Peering was deployed to.
| `vNetPeeringName` | The Name of the vNet Peering.
| `vNetPeeringResourceId` | The Resource Id of the vNet Peering.
## Considerations
N/A
## Additional resources
- [Microsoft.Network virtualNetworks/virtualNetworkPeerings template reference](https://docs.microsoft.com/en-us/azure/templates/microsoft.network/2019-04-01/virtualnetworks/virtualnetworkpeerings)

Просмотреть файл

Просмотреть файл

Просмотреть файл

Просмотреть файл

Просмотреть файл

@ -0,0 +1,168 @@
<#
.NOTES
==============================================================================================
Copyright(c) Microsoft Corporation. All rights reserved.
File: module.tests.ps1
Purpose: Pester - Test vNet Peering Templates
Version: 1.0.0.0 - 1st April 2019 - Azure Virtual Datacenter Development Team
==============================================================================================
.SYNOPSIS
This script contains functionality used to test vNet Peering ARM template syntax.
.DESCRIPTION
This script contains functionality used to test vNet Peering ARM template syntax.
Deployment steps of the script are outlined below.
1) Test Template File Syntax
2) Test Parameter File Syntax
3) Test Template and Parameter File Compactibility
#>
#Requires -Version 5
#region - Parameters
$here = Split-Path -Parent $MyInvocation.MyCommand.Path
$here = Join-Path $here ".."
$template = Split-Path -Leaf $here
$TemplateFileTestCases = @()
ForEach ( $File in (Get-ChildItem (Join-Path "$here" "deploy.json") -Recurse | Select-Object -ExpandProperty Name) ) {
$TemplateFileTestCases += @{ TemplateFile = $File }
}
$ParameterFileTestCases = @()
ForEach ( $File in (Get-ChildItem (Join-Path "$here" "Tests" -AdditionalChildPath @("*parameters.json")) -Recurse -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Name) ) {
$ParameterFileTestCases += @{ ParameterFile = Join-Path "Tests" $File }
}
$Modules = @();
ForEach ( $File in (Get-ChildItem (Join-Path "$here" "deploy.json") ) ) {
$Module = [PSCustomObject]@{
'Template' = $null
'Parameters' = $null
}
$Module.Template = $File.FullName;
$Parameters = @();
ForEach ( $ParameterFile in (Get-ChildItem (Join-Path "$here" "Tests" -AdditionalChildPath @("*parameters.json")) -Recurse -ErrorAction SilentlyContinue| Select-Object -ExpandProperty Name) ) {
$Parameters += (Join-Path "$here" "Tests" -AdditionalChildPath @("$ParameterFile") )
}
$Module.Parameters = $Parameters;
$Modules += @{ Module = $Module };
}
#endregion
#region - Run Pester Test Script
Describe "Template: $template - vNetPeering" -Tags Unit {
Context "Template File Syntax" {
It "Has a JSON template file" {
(Join-Path "$here" "*deploy.json") | Should Exist
}
It "Converts from JSON and has the expected properties" -TestCases $TemplateFileTestCases {
Param ($TemplateFile)
$expectedProperties = '$schema',
'contentVersion',
'parameters',
'variables',
'resources',
'outputs'| Sort-Object
$templateProperties = (Get-Content (Join-Path "$here" "$TemplateFile") `
| ConvertFrom-Json -ErrorAction SilentlyContinue) `
| Get-Member -MemberType NoteProperty `
| Sort-Object -Property Name `
| ForEach-Object Name
$templateProperties | Should Be $expectedProperties
}
}
Context "Parameter File Syntax" {
It "Parameter file does not contains the expected properties" -TestCases $ParameterFileTestCases {
Param( $ParameterFile )
$expectedProperties = '$schema',
'contentVersion',
'parameters' | Sort-Object
Write-Host $ParameterFile
Join-Path "$here" "$ParameterFile" | Write-Host
$templateFileProperties = (Get-Content (Join-Path "$here" "$ParameterFile") `
| ConvertFrom-Json -ErrorAction SilentlyContinue) `
| Get-Member -MemberType NoteProperty `
| Sort-Object -Property Name `
| ForEach-Object Name
$templateFileProperties | Should Be $expectedProperties
}
}
Context "Template and Parameter Compactibility" {
It "Is count of required parameters in template file equal or lesser than count of all parameters in parameters file" -TestCases $Modules {
Param( $Module )
$requiredParametersInTemplateFile = (Get-Content "$($Module.Template)" `
| ConvertFrom-Json -ErrorAction SilentlyContinue).Parameters.PSObject.Properties `
| Where-Object -FilterScript { -not ($_.Value.PSObject.Properties.Name -eq "defaultValue") } `
| Sort-Object -Property Name `
| ForEach-Object Name
ForEach ( $Parameter in $Module.Parameters ) {
$allParametersInParametersFile = (Get-Content $Parameter `
| ConvertFrom-Json -ErrorAction SilentlyContinue).Parameters.PSObject.Properties `
| Sort-Object -Property Name `
| ForEach-Object Name
if ($requiredParametersInTemplateFile.Count -gt $allParametersInParametersFile.Count) {
Write-Host "Mismatch found, parameters from parameter file are more than the expected in the template"
Write-Host "Required parameters are: $(ConvertTo-Json $requiredParametersInTemplateFile)"
Write-Host "Parameters from parameter file are: $(ConvertTo-Json $allParametersInParametersFile)"
}
$requiredParametersInTemplateFile.Count | Should Not BeGreaterThan $allParametersInParametersFile.Count;
}
}
It "Has all parameters in parameters file existing in template file" -TestCases $Modules {
Param( $Module )
$allParametersInTemplateFile = (Get-Content "$($Module.Template)" `
| ConvertFrom-Json -ErrorAction SilentlyContinue).Parameters.PSObject.Properties `
| Sort-Object -Property Name `
| ForEach-Object Name
ForEach ( $Parameter in $Module.Parameters ) {
Write-Host "File analyzed: $Parameter";
$allParametersInParametersFile = (Get-Content $Parameter `
| ConvertFrom-Json -ErrorAction SilentlyContinue).Parameters.PSObject.Properties `
| Sort-Object -Property Name `
| ForEach-Object Name
$result = @($allParametersInParametersFile| Where-Object {$allParametersInTemplateFile -notcontains $_});
Write-Host "Invalid parameters: $(ConvertTo-Json $result)";
@($allParametersInParametersFile| Where-Object {$allParametersInTemplateFile -notcontains $_}).Count | Should Be 0;
}
}
It "Has required parameters in template file existing in parameters file" -TestCases $Modules {
Param( $Module )
$requiredParametersInTemplateFile = (Get-Content "$($Module.Template)" `
| ConvertFrom-Json -ErrorAction SilentlyContinue).Parameters.PSObject.Properties `
| Where-Object -FilterScript { -not ($_.Value.PSObject.Properties.Name -eq "defaultValue") } `
| Sort-Object -Property Name `
| ForEach-Object Name
ForEach ( $Parameter in $Module.Parameters ) {
$allParametersInParametersFile = (Get-Content $Parameter `
| ConvertFrom-Json -ErrorAction SilentlyContinue).Parameters.PSObject.Properties `
| Sort-Object -Property Name `
| ForEach-Object Name
$invalid = $requiredParametersInTemplateFile | Where-Object {$allParametersInParametersFile -notcontains $_}
if ($invalid.Count -gt 0) {
Write-Host "Invalid parameters: $(ConvertTo-Json $invalid)"
}
@($requiredParametersInTemplateFile | Where-Object {$allParametersInParametersFile -notcontains $_}).Count | Should Be 0;
}
}
}
}
#endregion

Просмотреть файл

@ -0,0 +1,15 @@
{
"$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"localVnetName": {
"value": "shared-vdc-vnet"
},
"peeringName": {
"value": "appPeering"
},
"remoteVirtualNetworkId": {
"value": "/subscriptions/{subscription-id}/resourceGroups/{resource-group}/providers/Microsoft.Network/virtualNetworks/{nvet-name}"
}
}
}

Просмотреть файл

@ -0,0 +1,163 @@
<#
.NOTES
==============================================================================================
Copyright(c) Microsoft Corporation. All rights reserved.
File: module.tests.ps1
Purpose: Pester - Test Virtual Network Peering ARM Templates
Version: 1.0.0.0 - 1st April 2019 - Azure Virtual Datacenter Development Team
==============================================================================================
.SYNOPSIS
This script contains functionality used to test Azure Virtual Network Peering ARM template synatax.
.DESCRIPTION
This script contains functionality used to test Azure Virtual Network Peering ARM template synatax.
Deployment steps of the script are outlined below.
1) Test Template File Syntax
2) Test Parameter File Syntax
3) Test Template and Parameter File Compactibility
#>
#Requires -Version 5
#region Parameters
$here = Split-Path -Parent $MyInvocation.MyCommand.Path
$here = Join-Path $here ".."
$template = Split-Path -Leaf $here
$TemplateFileTestCases = @()
ForEach ( $File in (Get-ChildItem (Join-Path "$here" "Policy" -AdditionalChildPath @("deploy.json")) -Recurse -ErrorAction SilentlyContinue| Select-Object -ExpandProperty Name) ) {
$TemplateFileTestCases += @{ TemplateFile = $File }
}
$ParameterFileTestCases = @()
ForEach ( $File in (Get-ChildItem (Join-Path "$here" "Policy" -AdditionalChildPath @("*parameters.json")) -Recurse -ErrorAction SilentlyContinue| Select-Object -ExpandProperty Name) ) {
$ParameterFileTestCases += @{ ParameterFile = Join-Path "Policy" $File }
}
$Modules = @();
ForEach ( $File in (Get-ChildItem (Join-Path "$here" "Policy" -AdditionalChildPath @("deploy.json")) -ErrorAction SilentlyContinue ) ) {
$Module = [PSCustomObject]@{
'Template' = $null
'Parameters' = $null
}
$Module.Template = $File.FullName;
$Parameters = @();
ForEach ( $ParameterFile in (Get-ChildItem (Join-Path "$here" "Policy" -AdditionalChildPath @("*parameters.json")) -Recurse -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Name) ) {
$Parameters += (Join-Path "$here" "Policy" -AdditionalChildPath @("$ParameterFile") )
}
$Module.Parameters = $Parameters;
$Modules += @{ Module = $Module };
}
#endregion
if ($null -ne $TemplateFileTestCases -and
$TemplateFileTestCases.Count -gt 0) {
#region Run Pester Test Script
Describe "Template: $template - Virtual Network Peering" -Tags Unit {
Context "Template File Syntax" {
It "Has a JSON template file" {
(Join-Path "$here" "deploy.json") | Should Exist
}
It "Converts from JSON and has the expected properties" -TestCases $TemplateFileTestCases {
Param( $TemplateFile )
$expectedProperties = '$schema',
'contentVersion',
'parameters',
'variables',
'resources',
'outputs' | Sort-Object
$templateProperties = (Get-Content (Join-Path "$here" "$TemplateFile") `
| ConvertFrom-Json -ErrorAction SilentlyContinue) `
| Get-Member -MemberType NoteProperty `
| Sort-Object -Property Name `
| ForEach-Object Name
$templateProperties | Should Be $expectedProperties
}
}
Context "Parameter File Syntax" {
It "Parameter file does not contains the expected properties" -TestCases $ParameterFileTestCases {
Param( $ParameterFile )
$expectedProperties = '$schema',
'contentVersion',
'parameters' | Sort-Object
Write-Host $ParameterFile
Join-Path "$here" "$ParameterFile" | Write-Host
$templateFileProperties = (Get-Content (Join-Path "$here" "$ParameterFile") `
| ConvertFrom-Json -ErrorAction SilentlyContinue) `
| Get-Member -MemberType NoteProperty `
| Sort-Object -Property Name `
| ForEach-Object Name
$templateFileProperties | Should Be $expectedProperties
}
}
Context "Template and Parameter Compactibility" {
It "Is count of required parameters in template file equal or lesser than count of all parameters in parameters file" -TestCases $Modules {
Param( $Module )
$requiredParametersInTemplateFile = (Get-Content "$($Module.Template)" `
| ConvertFrom-Json -ErrorAction SilentlyContinue).Parameters.PSObject.Properties `
| Where-Object -FilterScript { -not ($_.Value.PSObject.Properties.Name -eq "defaultValue") } `
| Sort-Object -Property Name `
| ForEach-Object Name
ForEach ( $Parameter in $Module.Parameters ) {
$allParametersInParametersFile = (Get-Content $Parameter `
| ConvertFrom-Json -ErrorAction SilentlyContinue).Parameters.PSObject.Properties `
| Sort-Object -Property Name `
| ForEach-Object Name
$requiredParametersInTemplateFile.Count | Should Not BeGreaterThan $allParametersInParametersFile.Count;
}
}
It "Has all parameters in parameters file existing in template file" -TestCases $Modules {
Param( $Module )
$allParametersInTemplateFile = (Get-Content "$($Module.Template)" `
| ConvertFrom-Json -ErrorAction SilentlyContinue).Parameters.PSObject.Properties `
| Sort-Object -Property Name `
| ForEach-Object Name
ForEach ( $Parameter in $Module.Parameters ) {
Write-Host "File analyzed: $Parameter";
$allParametersInParametersFile = (Get-Content $Parameter `
| ConvertFrom-Json -ErrorAction SilentlyContinue).Parameters.PSObject.Properties `
| Sort-Object -Property Name `
| ForEach-Object Name
$result = @($allParametersInParametersFile| Where-Object {$allParametersInTemplateFile -notcontains $_});
Write-Host "Invalid parameters: $(ConvertTo-Json $result)";
@($allParametersInParametersFile| Where-Object {$allParametersInTemplateFile -notcontains $_}).Count | Should Be 0;
}
}
It "Has required parameters in template file existing in parameters file" -TestCases $Modules {
Param( $Module )
$requiredParametersInTemplateFile = (Get-Content "$($Module.Template)" `
| ConvertFrom-Json -ErrorAction SilentlyContinue).Parameters.PSObject.Properties `
| Where-Object -FilterScript { -not ($_.Value.PSObject.Properties.Name -eq "defaultValue") } `
| Sort-Object -Property Name `
| ForEach-Object Name
ForEach ( $Parameter in $Module.Parameters ) {
$allParametersInParametersFile = (Get-Content $Parameter `
| ConvertFrom-Json -ErrorAction SilentlyContinue).Parameters.PSObject.Properties `
| Sort-Object -Property Name `
| ForEach-Object Name
@($requiredParametersInTemplateFile| Where-Object {$allParametersInParametersFile -notcontains $_}).Count | Should Be 0;
}
}
}
}
#endregion
}

Просмотреть файл

@ -0,0 +1,163 @@
<#
.NOTES
==============================================================================================
Copyright(c) Microsoft Corporation. All rights reserved.
File: module.tests.ps1
Purpose: Pester - Test Virtual Network Peering ARM Templates
Version: 1.0.0.0 - 1st April 2019 - Azure Virtual Datacenter Development Team
==============================================================================================
.SYNOPSIS
This script contains functionality used to test Azure Virtual Network Peering ARM template synatax.
.DESCRIPTION
This script contains functionality used to test Azure Virtual Network Peering ARM template synatax.
Deployment steps of the script are outlined below.
1) Test Template File Syntax
2) Test Parameter File Syntax
3) Test Template and Parameter File Compactibility
#>
#Requires -Version 5
#region Parameters
$here = Split-Path -Parent $MyInvocation.MyCommand.Path
$here = Join-Path $here ".."
$template = Split-Path -Leaf $here
$TemplateFileTestCases = @()
ForEach ( $File in (Get-ChildItem (Join-Path "$here" "RBAC" -AdditionalChildPath @("deploy.json")) -ErrorAction SilentlyContinue -Recurse | Select-Object -ExpandProperty Name) ) {
$TemplateFileTestCases += @{ TemplateFile = $File }
}
$ParameterFileTestCases = @()
ForEach ( $File in (Get-ChildItem (Join-Path "$here" "RBAC" -AdditionalChildPath @("*parameters.json")) -ErrorAction SilentlyContinue -Recurse | Select-Object -ExpandProperty Name) ) {
$ParameterFileTestCases += @{ ParameterFile = Join-Path "RBAC" $File }
}
$Modules = @();
ForEach ( $File in (Get-ChildItem (Join-Path "$here" "RBAC" -AdditionalChildPath @("deploy.json")) -ErrorAction SilentlyContinue ) ) {
$Module = [PSCustomObject]@{
'Template' = $null
'Parameters' = $null
}
$Module.Template = $File.FullName;
$Parameters = @();
ForEach ( $ParameterFile in (Get-ChildItem (Join-Path "$here" "RBAC" -AdditionalChildPath @("*parameters.json")) -Recurse | Select-Object -ExpandProperty Name) ) {
$Parameters += (Join-Path "$here" "RBAC" -AdditionalChildPath @("$ParameterFile") )
}
$Module.Parameters = $Parameters;
$Modules += @{ Module = $Module };
}
#endregion
if ($null -ne $TemplateFileTestCases -and
$TemplateFileTestCases.Count -gt 0) {
#region Run Pester Test Script
Describe "Template: $template - Virtual Network Peering" -Tags Unit {
Context "Template File Syntax" {
It "Has a JSON template file" -TestCases $TemplateFileTestCases {
(Join-Path "$here" "deploy.json") | Should Exist
}
It "Converts from JSON and has the expected properties" -TestCases $TemplateFileTestCases {
Param( $TemplateFile )
Write-Host "TF: $TemplateFile"
$expectedProperties = '$schema',
'contentVersion',
'parameters',
'variables',
'resources',
'outputs' | Sort-Object
$templateProperties = (Get-Content (Join-Path "$here" "$TemplateFile") `
| ConvertFrom-Json -ErrorAction SilentlyContinue) `
| Get-Member -MemberType NoteProperty `
| Sort-Object -Property Name `
| ForEach-Object Name
$templateProperties | Should Be $expectedProperties
}
}
Context "Parameter File Syntax" {
It "Parameter file does not contains the expected properties" -TestCases $ParameterFileTestCases {
Param( $ParameterFile )
$expectedProperties = '$schema',
'contentVersion',
'parameters' | Sort-Object
Write-Host $ParameterFile
Join-Path "$here" "$ParameterFile" | Write-Host
$templateFileProperties = (Get-Content (Join-Path "$here" "$ParameterFile") `
| ConvertFrom-Json -ErrorAction SilentlyContinue) `
| Get-Member -MemberType NoteProperty `
| Sort-Object -Property Name `
| ForEach-Object Name
$templateFileProperties | Should Be $expectedProperties
}
}
Context "Template and Parameter Compactibility" {
It "Is count of required parameters in template file equal or lesser than count of all parameters in parameters file" -TestCases $Modules {
Param( $Module )
$requiredParametersInTemplateFile = (Get-Content "$($Module.Template)" `
| ConvertFrom-Json -ErrorAction SilentlyContinue).Parameters.PSObject.Properties `
| Where-Object -FilterScript { -not ($_.Value.PSObject.Properties.Name -eq "defaultValue") } `
| Sort-Object -Property Name `
| ForEach-Object Name
ForEach ( $Parameter in $Module.Parameters ) {
$allParametersInParametersFile = (Get-Content $Parameter `
| ConvertFrom-Json -ErrorAction SilentlyContinue).Parameters.PSObject.Properties `
| Sort-Object -Property Name `
| ForEach-Object Name
$requiredParametersInTemplateFile.Count | Should Not BeGreaterThan $allParametersInParametersFile.Count;
}
}
It "Has all parameters in parameters file existing in template file" -TestCases $Modules {
Param( $Module )
$allParametersInTemplateFile = (Get-Content "$($Module.Template)" `
| ConvertFrom-Json -ErrorAction SilentlyContinue).Parameters.PSObject.Properties `
| Sort-Object -Property Name `
| ForEach-Object Name
ForEach ( $Parameter in $Module.Parameters ) {
Write-Host "File analyzed: $Parameter";
$allParametersInParametersFile = (Get-Content $Parameter `
| ConvertFrom-Json -ErrorAction SilentlyContinue).Parameters.PSObject.Properties `
| Sort-Object -Property Name `
| ForEach-Object Name
$result = @($allParametersInParametersFile| Where-Object {$allParametersInTemplateFile -notcontains $_});
Write-Host "Invalid parameters: $(ConvertTo-Json $result)";
@($allParametersInParametersFile| Where-Object {$allParametersInTemplateFile -notcontains $_}).Count | Should Be 0;
}
}
It "Has required parameters in template file existing in parameters file" -TestCases $Modules {
Param( $Module )
$requiredParametersInTemplateFile = (Get-Content "$($Module.Template)" `
| ConvertFrom-Json -ErrorAction SilentlyContinue).Parameters.PSObject.Properties `
| Where-Object -FilterScript { -not ($_.Value.PSObject.Properties.Name -eq "defaultValue") } `
| Sort-Object -Property Name `
| ForEach-Object Name
ForEach ( $Parameter in $Module.Parameters ) {
$allParametersInParametersFile = (Get-Content $Parameter `
| ConvertFrom-Json -ErrorAction SilentlyContinue).Parameters.PSObject.Properties `
| Sort-Object -Property Name `
| ForEach-Object Name
@($requiredParametersInTemplateFile| Where-Object {$allParametersInParametersFile -notcontains $_}).Count | Should Be 0;
}
}
}
}
#endregion
}

Просмотреть файл

@ -0,0 +1,63 @@
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"hostPoolId": {
"type": "String",
"metadata": {
"description": "Windows Virtual Desktop host pool name."
}
},
"baseTime":{
"type":"string",
"defaultValue": "[utcNow('u')]",
"metadata": {
"description": "Do not change. This parameter is used for the expiry time of the registration code."
}
},
"location": {
"type": "string",
"defaultValue": "[resourceGroup().location]",
"metadata": {
"description": "Azure location for the host pool."
}
},
"applicationGroupName": {
"type": "string",
"defaultValue": "appGroup",
"metadata": {
"description": "App group name for the application group."
}
},
"appGroupType": {
"type": "string",
"defaultValue": "Desktop",
"metadata": {
"description": "App group type. Can Be Desktop or RemoteApp for the application group."
}
}
},
"variables": {
},
"resources": [
{
"type": "Microsoft.DesktopVirtualization/applicationgroups",
"apiVersion": "2019-12-10-preview",
"name": "[parameters('applicationGroupName')]",
"location": "[parameters('location')]",
"kind": "[parameters('appGroupType')]",
"properties": {
"hostPoolArmPath": "[parameters('hostPoolId')]",
"description": "Desktop Application Group created through the ARM",
"applicationGroupType": "[parameters('appGroupType')]"
}
}
],
"outputs": {
}
}

Просмотреть файл

@ -0,0 +1,35 @@
# Host Pool application group for windows virtual desktop
This template deploys an application group to a host pool.
## Resources
- Microsoft.Network/virtualNetworks/virtualNetworkPeerings
## Parameters
| Parameter Name | Default Value | Description |
| :- | :- | :- |
| `localVnetName` | | Required. The Name of the Virtual Network to add the peering to.
| `peeringName` | | Require. The Name of the virtual network peering resource.
| `remoteVirtualNetworkId` | | Required. The Resource Id of the remote virtual network. The remove virtual network can be in the same or different region.
| `allowVirtualNetworkAccess` | `true` | Optional. Whether the VMs in the local virtual network space would be able to access the VMs in remote virtual network space.
| `allowForwardedTraffic` | | Optional. Whether the forwarded traffic from the VMs in the local virtual network will be allowed/disallowed in remote virtual network.
| `allowGatewayTransit` | | Optional. If gateway links can be used in remote virtual networking to link to this virtual network.
| `useRemoteGateways` | | Optional. If remote gateways can be used on this virtual network. If the flag is set to true, and allowGatewayTransit on remote peering is also true, virtual network will use gateways of remote virtual network for transit. Only one peering can have this flag set to true. This flag cannot be set if virtual network already has a gateway.
## Outputs
| Output Name | Description |
| :- | :- |
| `vNetPeeringResourceResourceGroup` | The Resource Group the vNet Peering was deployed to.
| `vNetPeeringName` | The Name of the vNet Peering.
| `vNetPeeringResourceId` | The Resource Id of the vNet Peering.
## Considerations
N/A
## Additional resources
- [Microsoft.Network virtualNetworks/virtualNetworkPeerings template reference](https://docs.microsoft.com/en-us/azure/templates/microsoft.network/2019-04-01/virtualnetworks/virtualnetworkpeerings)

Просмотреть файл

Просмотреть файл

Просмотреть файл

Просмотреть файл

Просмотреть файл

@ -0,0 +1,168 @@
<#
.NOTES
==============================================================================================
Copyright(c) Microsoft Corporation. All rights reserved.
File: module.tests.ps1
Purpose: Pester - Test vNet Peering Templates
Version: 1.0.0.0 - 1st April 2019 - Azure Virtual Datacenter Development Team
==============================================================================================
.SYNOPSIS
This script contains functionality used to test vNet Peering ARM template syntax.
.DESCRIPTION
This script contains functionality used to test vNet Peering ARM template syntax.
Deployment steps of the script are outlined below.
1) Test Template File Syntax
2) Test Parameter File Syntax
3) Test Template and Parameter File Compactibility
#>
#Requires -Version 5
#region - Parameters
$here = Split-Path -Parent $MyInvocation.MyCommand.Path
$here = Join-Path $here ".."
$template = Split-Path -Leaf $here
$TemplateFileTestCases = @()
ForEach ( $File in (Get-ChildItem (Join-Path "$here" "deploy.json") -Recurse | Select-Object -ExpandProperty Name) ) {
$TemplateFileTestCases += @{ TemplateFile = $File }
}
$ParameterFileTestCases = @()
ForEach ( $File in (Get-ChildItem (Join-Path "$here" "Tests" -AdditionalChildPath @("*parameters.json")) -Recurse -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Name) ) {
$ParameterFileTestCases += @{ ParameterFile = Join-Path "Tests" $File }
}
$Modules = @();
ForEach ( $File in (Get-ChildItem (Join-Path "$here" "deploy.json") ) ) {
$Module = [PSCustomObject]@{
'Template' = $null
'Parameters' = $null
}
$Module.Template = $File.FullName;
$Parameters = @();
ForEach ( $ParameterFile in (Get-ChildItem (Join-Path "$here" "Tests" -AdditionalChildPath @("*parameters.json")) -Recurse -ErrorAction SilentlyContinue| Select-Object -ExpandProperty Name) ) {
$Parameters += (Join-Path "$here" "Tests" -AdditionalChildPath @("$ParameterFile") )
}
$Module.Parameters = $Parameters;
$Modules += @{ Module = $Module };
}
#endregion
#region - Run Pester Test Script
Describe "Template: $template - vNetPeering" -Tags Unit {
Context "Template File Syntax" {
It "Has a JSON template file" {
(Join-Path "$here" "*deploy.json") | Should Exist
}
It "Converts from JSON and has the expected properties" -TestCases $TemplateFileTestCases {
Param ($TemplateFile)
$expectedProperties = '$schema',
'contentVersion',
'parameters',
'variables',
'resources',
'outputs'| Sort-Object
$templateProperties = (Get-Content (Join-Path "$here" "$TemplateFile") `
| ConvertFrom-Json -ErrorAction SilentlyContinue) `
| Get-Member -MemberType NoteProperty `
| Sort-Object -Property Name `
| ForEach-Object Name
$templateProperties | Should Be $expectedProperties
}
}
Context "Parameter File Syntax" {
It "Parameter file does not contains the expected properties" -TestCases $ParameterFileTestCases {
Param( $ParameterFile )
$expectedProperties = '$schema',
'contentVersion',
'parameters' | Sort-Object
Write-Host $ParameterFile
Join-Path "$here" "$ParameterFile" | Write-Host
$templateFileProperties = (Get-Content (Join-Path "$here" "$ParameterFile") `
| ConvertFrom-Json -ErrorAction SilentlyContinue) `
| Get-Member -MemberType NoteProperty `
| Sort-Object -Property Name `
| ForEach-Object Name
$templateFileProperties | Should Be $expectedProperties
}
}
Context "Template and Parameter Compactibility" {
It "Is count of required parameters in template file equal or lesser than count of all parameters in parameters file" -TestCases $Modules {
Param( $Module )
$requiredParametersInTemplateFile = (Get-Content "$($Module.Template)" `
| ConvertFrom-Json -ErrorAction SilentlyContinue).Parameters.PSObject.Properties `
| Where-Object -FilterScript { -not ($_.Value.PSObject.Properties.Name -eq "defaultValue") } `
| Sort-Object -Property Name `
| ForEach-Object Name
ForEach ( $Parameter in $Module.Parameters ) {
$allParametersInParametersFile = (Get-Content $Parameter `
| ConvertFrom-Json -ErrorAction SilentlyContinue).Parameters.PSObject.Properties `
| Sort-Object -Property Name `
| ForEach-Object Name
if ($requiredParametersInTemplateFile.Count -gt $allParametersInParametersFile.Count) {
Write-Host "Mismatch found, parameters from parameter file are more than the expected in the template"
Write-Host "Required parameters are: $(ConvertTo-Json $requiredParametersInTemplateFile)"
Write-Host "Parameters from parameter file are: $(ConvertTo-Json $allParametersInParametersFile)"
}
$requiredParametersInTemplateFile.Count | Should Not BeGreaterThan $allParametersInParametersFile.Count;
}
}
It "Has all parameters in parameters file existing in template file" -TestCases $Modules {
Param( $Module )
$allParametersInTemplateFile = (Get-Content "$($Module.Template)" `
| ConvertFrom-Json -ErrorAction SilentlyContinue).Parameters.PSObject.Properties `
| Sort-Object -Property Name `
| ForEach-Object Name
ForEach ( $Parameter in $Module.Parameters ) {
Write-Host "File analyzed: $Parameter";
$allParametersInParametersFile = (Get-Content $Parameter `
| ConvertFrom-Json -ErrorAction SilentlyContinue).Parameters.PSObject.Properties `
| Sort-Object -Property Name `
| ForEach-Object Name
$result = @($allParametersInParametersFile| Where-Object {$allParametersInTemplateFile -notcontains $_});
Write-Host "Invalid parameters: $(ConvertTo-Json $result)";
@($allParametersInParametersFile| Where-Object {$allParametersInTemplateFile -notcontains $_}).Count | Should Be 0;
}
}
It "Has required parameters in template file existing in parameters file" -TestCases $Modules {
Param( $Module )
$requiredParametersInTemplateFile = (Get-Content "$($Module.Template)" `
| ConvertFrom-Json -ErrorAction SilentlyContinue).Parameters.PSObject.Properties `
| Where-Object -FilterScript { -not ($_.Value.PSObject.Properties.Name -eq "defaultValue") } `
| Sort-Object -Property Name `
| ForEach-Object Name
ForEach ( $Parameter in $Module.Parameters ) {
$allParametersInParametersFile = (Get-Content $Parameter `
| ConvertFrom-Json -ErrorAction SilentlyContinue).Parameters.PSObject.Properties `
| Sort-Object -Property Name `
| ForEach-Object Name
$invalid = $requiredParametersInTemplateFile | Where-Object {$allParametersInParametersFile -notcontains $_}
if ($invalid.Count -gt 0) {
Write-Host "Invalid parameters: $(ConvertTo-Json $invalid)"
}
@($requiredParametersInTemplateFile | Where-Object {$allParametersInParametersFile -notcontains $_}).Count | Should Be 0;
}
}
}
}
#endregion

Просмотреть файл

@ -0,0 +1,15 @@
{
"$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"localVnetName": {
"value": "shared-vdc-vnet"
},
"peeringName": {
"value": "appPeering"
},
"remoteVirtualNetworkId": {
"value": "/subscriptions/{subscription-id}/resourceGroups/{resource-group}/providers/Microsoft.Network/virtualNetworks/{nvet-name}"
}
}
}

Просмотреть файл

@ -0,0 +1,163 @@
<#
.NOTES
==============================================================================================
Copyright(c) Microsoft Corporation. All rights reserved.
File: module.tests.ps1
Purpose: Pester - Test Virtual Network Peering ARM Templates
Version: 1.0.0.0 - 1st April 2019 - Azure Virtual Datacenter Development Team
==============================================================================================
.SYNOPSIS
This script contains functionality used to test Azure Virtual Network Peering ARM template synatax.
.DESCRIPTION
This script contains functionality used to test Azure Virtual Network Peering ARM template synatax.
Deployment steps of the script are outlined below.
1) Test Template File Syntax
2) Test Parameter File Syntax
3) Test Template and Parameter File Compactibility
#>
#Requires -Version 5
#region Parameters
$here = Split-Path -Parent $MyInvocation.MyCommand.Path
$here = Join-Path $here ".."
$template = Split-Path -Leaf $here
$TemplateFileTestCases = @()
ForEach ( $File in (Get-ChildItem (Join-Path "$here" "Policy" -AdditionalChildPath @("deploy.json")) -Recurse -ErrorAction SilentlyContinue| Select-Object -ExpandProperty Name) ) {
$TemplateFileTestCases += @{ TemplateFile = $File }
}
$ParameterFileTestCases = @()
ForEach ( $File in (Get-ChildItem (Join-Path "$here" "Policy" -AdditionalChildPath @("*parameters.json")) -Recurse -ErrorAction SilentlyContinue| Select-Object -ExpandProperty Name) ) {
$ParameterFileTestCases += @{ ParameterFile = Join-Path "Policy" $File }
}
$Modules = @();
ForEach ( $File in (Get-ChildItem (Join-Path "$here" "Policy" -AdditionalChildPath @("deploy.json")) -ErrorAction SilentlyContinue ) ) {
$Module = [PSCustomObject]@{
'Template' = $null
'Parameters' = $null
}
$Module.Template = $File.FullName;
$Parameters = @();
ForEach ( $ParameterFile in (Get-ChildItem (Join-Path "$here" "Policy" -AdditionalChildPath @("*parameters.json")) -Recurse -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Name) ) {
$Parameters += (Join-Path "$here" "Policy" -AdditionalChildPath @("$ParameterFile") )
}
$Module.Parameters = $Parameters;
$Modules += @{ Module = $Module };
}
#endregion
if ($null -ne $TemplateFileTestCases -and
$TemplateFileTestCases.Count -gt 0) {
#region Run Pester Test Script
Describe "Template: $template - Virtual Network Peering" -Tags Unit {
Context "Template File Syntax" {
It "Has a JSON template file" {
(Join-Path "$here" "deploy.json") | Should Exist
}
It "Converts from JSON and has the expected properties" -TestCases $TemplateFileTestCases {
Param( $TemplateFile )
$expectedProperties = '$schema',
'contentVersion',
'parameters',
'variables',
'resources',
'outputs' | Sort-Object
$templateProperties = (Get-Content (Join-Path "$here" "$TemplateFile") `
| ConvertFrom-Json -ErrorAction SilentlyContinue) `
| Get-Member -MemberType NoteProperty `
| Sort-Object -Property Name `
| ForEach-Object Name
$templateProperties | Should Be $expectedProperties
}
}
Context "Parameter File Syntax" {
It "Parameter file does not contains the expected properties" -TestCases $ParameterFileTestCases {
Param( $ParameterFile )
$expectedProperties = '$schema',
'contentVersion',
'parameters' | Sort-Object
Write-Host $ParameterFile
Join-Path "$here" "$ParameterFile" | Write-Host
$templateFileProperties = (Get-Content (Join-Path "$here" "$ParameterFile") `
| ConvertFrom-Json -ErrorAction SilentlyContinue) `
| Get-Member -MemberType NoteProperty `
| Sort-Object -Property Name `
| ForEach-Object Name
$templateFileProperties | Should Be $expectedProperties
}
}
Context "Template and Parameter Compactibility" {
It "Is count of required parameters in template file equal or lesser than count of all parameters in parameters file" -TestCases $Modules {
Param( $Module )
$requiredParametersInTemplateFile = (Get-Content "$($Module.Template)" `
| ConvertFrom-Json -ErrorAction SilentlyContinue).Parameters.PSObject.Properties `
| Where-Object -FilterScript { -not ($_.Value.PSObject.Properties.Name -eq "defaultValue") } `
| Sort-Object -Property Name `
| ForEach-Object Name
ForEach ( $Parameter in $Module.Parameters ) {
$allParametersInParametersFile = (Get-Content $Parameter `
| ConvertFrom-Json -ErrorAction SilentlyContinue).Parameters.PSObject.Properties `
| Sort-Object -Property Name `
| ForEach-Object Name
$requiredParametersInTemplateFile.Count | Should Not BeGreaterThan $allParametersInParametersFile.Count;
}
}
It "Has all parameters in parameters file existing in template file" -TestCases $Modules {
Param( $Module )
$allParametersInTemplateFile = (Get-Content "$($Module.Template)" `
| ConvertFrom-Json -ErrorAction SilentlyContinue).Parameters.PSObject.Properties `
| Sort-Object -Property Name `
| ForEach-Object Name
ForEach ( $Parameter in $Module.Parameters ) {
Write-Host "File analyzed: $Parameter";
$allParametersInParametersFile = (Get-Content $Parameter `
| ConvertFrom-Json -ErrorAction SilentlyContinue).Parameters.PSObject.Properties `
| Sort-Object -Property Name `
| ForEach-Object Name
$result = @($allParametersInParametersFile| Where-Object {$allParametersInTemplateFile -notcontains $_});
Write-Host "Invalid parameters: $(ConvertTo-Json $result)";
@($allParametersInParametersFile| Where-Object {$allParametersInTemplateFile -notcontains $_}).Count | Should Be 0;
}
}
It "Has required parameters in template file existing in parameters file" -TestCases $Modules {
Param( $Module )
$requiredParametersInTemplateFile = (Get-Content "$($Module.Template)" `
| ConvertFrom-Json -ErrorAction SilentlyContinue).Parameters.PSObject.Properties `
| Where-Object -FilterScript { -not ($_.Value.PSObject.Properties.Name -eq "defaultValue") } `
| Sort-Object -Property Name `
| ForEach-Object Name
ForEach ( $Parameter in $Module.Parameters ) {
$allParametersInParametersFile = (Get-Content $Parameter `
| ConvertFrom-Json -ErrorAction SilentlyContinue).Parameters.PSObject.Properties `
| Sort-Object -Property Name `
| ForEach-Object Name
@($requiredParametersInTemplateFile| Where-Object {$allParametersInParametersFile -notcontains $_}).Count | Should Be 0;
}
}
}
}
#endregion
}

Просмотреть файл

@ -0,0 +1,163 @@
<#
.NOTES
==============================================================================================
Copyright(c) Microsoft Corporation. All rights reserved.
File: module.tests.ps1
Purpose: Pester - Test Virtual Network Peering ARM Templates
Version: 1.0.0.0 - 1st April 2019 - Azure Virtual Datacenter Development Team
==============================================================================================
.SYNOPSIS
This script contains functionality used to test Azure Virtual Network Peering ARM template synatax.
.DESCRIPTION
This script contains functionality used to test Azure Virtual Network Peering ARM template synatax.
Deployment steps of the script are outlined below.
1) Test Template File Syntax
2) Test Parameter File Syntax
3) Test Template and Parameter File Compactibility
#>
#Requires -Version 5
#region Parameters
$here = Split-Path -Parent $MyInvocation.MyCommand.Path
$here = Join-Path $here ".."
$template = Split-Path -Leaf $here
$TemplateFileTestCases = @()
ForEach ( $File in (Get-ChildItem (Join-Path "$here" "RBAC" -AdditionalChildPath @("deploy.json")) -ErrorAction SilentlyContinue -Recurse | Select-Object -ExpandProperty Name) ) {
$TemplateFileTestCases += @{ TemplateFile = $File }
}
$ParameterFileTestCases = @()
ForEach ( $File in (Get-ChildItem (Join-Path "$here" "RBAC" -AdditionalChildPath @("*parameters.json")) -ErrorAction SilentlyContinue -Recurse | Select-Object -ExpandProperty Name) ) {
$ParameterFileTestCases += @{ ParameterFile = Join-Path "RBAC" $File }
}
$Modules = @();
ForEach ( $File in (Get-ChildItem (Join-Path "$here" "RBAC" -AdditionalChildPath @("deploy.json")) -ErrorAction SilentlyContinue ) ) {
$Module = [PSCustomObject]@{
'Template' = $null
'Parameters' = $null
}
$Module.Template = $File.FullName;
$Parameters = @();
ForEach ( $ParameterFile in (Get-ChildItem (Join-Path "$here" "RBAC" -AdditionalChildPath @("*parameters.json")) -Recurse | Select-Object -ExpandProperty Name) ) {
$Parameters += (Join-Path "$here" "RBAC" -AdditionalChildPath @("$ParameterFile") )
}
$Module.Parameters = $Parameters;
$Modules += @{ Module = $Module };
}
#endregion
if ($null -ne $TemplateFileTestCases -and
$TemplateFileTestCases.Count -gt 0) {
#region Run Pester Test Script
Describe "Template: $template - Virtual Network Peering" -Tags Unit {
Context "Template File Syntax" {
It "Has a JSON template file" -TestCases $TemplateFileTestCases {
(Join-Path "$here" "deploy.json") | Should Exist
}
It "Converts from JSON and has the expected properties" -TestCases $TemplateFileTestCases {
Param( $TemplateFile )
Write-Host "TF: $TemplateFile"
$expectedProperties = '$schema',
'contentVersion',
'parameters',
'variables',
'resources',
'outputs' | Sort-Object
$templateProperties = (Get-Content (Join-Path "$here" "$TemplateFile") `
| ConvertFrom-Json -ErrorAction SilentlyContinue) `
| Get-Member -MemberType NoteProperty `
| Sort-Object -Property Name `
| ForEach-Object Name
$templateProperties | Should Be $expectedProperties
}
}
Context "Parameter File Syntax" {
It "Parameter file does not contains the expected properties" -TestCases $ParameterFileTestCases {
Param( $ParameterFile )
$expectedProperties = '$schema',
'contentVersion',
'parameters' | Sort-Object
Write-Host $ParameterFile
Join-Path "$here" "$ParameterFile" | Write-Host
$templateFileProperties = (Get-Content (Join-Path "$here" "$ParameterFile") `
| ConvertFrom-Json -ErrorAction SilentlyContinue) `
| Get-Member -MemberType NoteProperty `
| Sort-Object -Property Name `
| ForEach-Object Name
$templateFileProperties | Should Be $expectedProperties
}
}
Context "Template and Parameter Compactibility" {
It "Is count of required parameters in template file equal or lesser than count of all parameters in parameters file" -TestCases $Modules {
Param( $Module )
$requiredParametersInTemplateFile = (Get-Content "$($Module.Template)" `
| ConvertFrom-Json -ErrorAction SilentlyContinue).Parameters.PSObject.Properties `
| Where-Object -FilterScript { -not ($_.Value.PSObject.Properties.Name -eq "defaultValue") } `
| Sort-Object -Property Name `
| ForEach-Object Name
ForEach ( $Parameter in $Module.Parameters ) {
$allParametersInParametersFile = (Get-Content $Parameter `
| ConvertFrom-Json -ErrorAction SilentlyContinue).Parameters.PSObject.Properties `
| Sort-Object -Property Name `
| ForEach-Object Name
$requiredParametersInTemplateFile.Count | Should Not BeGreaterThan $allParametersInParametersFile.Count;
}
}
It "Has all parameters in parameters file existing in template file" -TestCases $Modules {
Param( $Module )
$allParametersInTemplateFile = (Get-Content "$($Module.Template)" `
| ConvertFrom-Json -ErrorAction SilentlyContinue).Parameters.PSObject.Properties `
| Sort-Object -Property Name `
| ForEach-Object Name
ForEach ( $Parameter in $Module.Parameters ) {
Write-Host "File analyzed: $Parameter";
$allParametersInParametersFile = (Get-Content $Parameter `
| ConvertFrom-Json -ErrorAction SilentlyContinue).Parameters.PSObject.Properties `
| Sort-Object -Property Name `
| ForEach-Object Name
$result = @($allParametersInParametersFile| Where-Object {$allParametersInTemplateFile -notcontains $_});
Write-Host "Invalid parameters: $(ConvertTo-Json $result)";
@($allParametersInParametersFile| Where-Object {$allParametersInTemplateFile -notcontains $_}).Count | Should Be 0;
}
}
It "Has required parameters in template file existing in parameters file" -TestCases $Modules {
Param( $Module )
$requiredParametersInTemplateFile = (Get-Content "$($Module.Template)" `
| ConvertFrom-Json -ErrorAction SilentlyContinue).Parameters.PSObject.Properties `
| Where-Object -FilterScript { -not ($_.Value.PSObject.Properties.Name -eq "defaultValue") } `
| Sort-Object -Property Name `
| ForEach-Object Name
ForEach ( $Parameter in $Module.Parameters ) {
$allParametersInParametersFile = (Get-Content $Parameter `
| ConvertFrom-Json -ErrorAction SilentlyContinue).Parameters.PSObject.Properties `
| Sort-Object -Property Name `
| ForEach-Object Name
@($requiredParametersInTemplateFile| Where-Object {$allParametersInParametersFile -notcontains $_}).Count | Should Be 0;
}
}
}
}
#endregion
}

Просмотреть файл

@ -0,0 +1,92 @@
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"hostPoolName": {
"type": "String",
"metadata": {
"description": "Windows Virtual Desktop host pool name."
}
},
"baseTime":{
"type":"string",
"defaultValue": "[utcNow('u')]",
"metadata": {
"description": "Do not change. This parameter is used for the expiry time of the registration code."
}
},
"location": {
"type": "string",
"defaultValue": "[resourceGroup().location]",
"metadata": {
"description": "Azure location for the host pool."
}
},
"hostPoolType": {
"type": "string",
"metadata": {
"description": "Host Pool Type. This can be personal or pooled."
}
},
"assignmentType": {
"type": "string",
"defaultValue": "",
"metadata": {
"description": "Optional. Only for Personal host pool type."
}
},
"maxSessionLimit": {
"type": "string",
"defaultValue": "1000",
"metadata": {
"description": "Optional. The maximum number of users that have concurrent sessions on a session host."
}
},
"loadBalancingAlgorithm": {
"type": "string",
"metadata": {
"description": "Optional - Pooled host type. Breadth-first load balancing distributes new user sessions across all available session hosts in the host pool. Depth-first load balancing distributes new user sessions to an available session host with the highest number of connections but has not reached its maximum session limit threshold."
}
}
},
"variables": {
"expireTime": "[dateTimeAdd(parameters('baseTime'), 'P15D')]"
},
"resources": [
{
"type": "Microsoft.DesktopVirtualization/hostpools",
"apiVersion": "2019-12-10-preview",
"name": "[parameters('hostPoolName')]",
"location": "[parameters('location')]",
"properties": {
"description": "Created through the WVD extension",
"hostPoolType": "[parameters('hostPoolType')]",
"personalDesktopAssignmentType": "[parameters('assignmentType')]",
"maxSessionLimit": "[parameters('maxSessionLimit')]",
"loadBalancerType": "[parameters('loadBalancingAlgorithm')]",
"registrationInfo": {
"expirationTime": "[variables('expireTime')]",
"registrationTokenOperation": "update"
}
}
}
],
"outputs": {
"registrationInfo": {
"value": "[reference(parameters('hostPoolName')).registrationInfo.Token]",
"type": "string"
},
"hostPoolName": {
"value": "[parameters('hostPoolName')]",
"type": "string"
},
"hostPoolID": {
"value": "[resourceId('Microsoft.DesktopVirtualization/hostpools', parameters('hostPoolName'))]",
"type": "string"
}
}
}

Просмотреть файл

@ -0,0 +1,35 @@
# Host Pool for windows virtual desktop
This template deploys a host pool for wvd.
## Resources
- Microsoft.Network/virtualNetworks/virtualNetworkPeerings
## Parameters
| Parameter Name | Default Value | Description |
| :- | :- | :- |
| `localVnetName` | | Required. The Name of the Virtual Network to add the peering to.
| `peeringName` | | Require. The Name of the virtual network peering resource.
| `remoteVirtualNetworkId` | | Required. The Resource Id of the remote virtual network. The remove virtual network can be in the same or different region.
| `allowVirtualNetworkAccess` | `true` | Optional. Whether the VMs in the local virtual network space would be able to access the VMs in remote virtual network space.
| `allowForwardedTraffic` | | Optional. Whether the forwarded traffic from the VMs in the local virtual network will be allowed/disallowed in remote virtual network.
| `allowGatewayTransit` | | Optional. If gateway links can be used in remote virtual networking to link to this virtual network.
| `useRemoteGateways` | | Optional. If remote gateways can be used on this virtual network. If the flag is set to true, and allowGatewayTransit on remote peering is also true, virtual network will use gateways of remote virtual network for transit. Only one peering can have this flag set to true. This flag cannot be set if virtual network already has a gateway.
## Outputs
| Output Name | Description |
| :- | :- |
| `vNetPeeringResourceResourceGroup` | The Resource Group the vNet Peering was deployed to.
| `vNetPeeringName` | The Name of the vNet Peering.
| `vNetPeeringResourceId` | The Resource Id of the vNet Peering.
## Considerations
N/A
## Additional resources
- [Microsoft.Network virtualNetworks/virtualNetworkPeerings template reference](https://docs.microsoft.com/en-us/azure/templates/microsoft.network/2019-04-01/virtualnetworks/virtualnetworkpeerings)

Просмотреть файл

@ -1,6 +1,6 @@
# Modules 2.0
A module is a reusable set of artifacts that can be composed into an archetype.
A module is a reusable set of artifacts that can be composed into an archetype (environment).
The modules can be deployed by anything that can deploy ARM templates. There is no need for a proprietary tool for deploying a single module.
This document is about the 2.0 format for modules.

Просмотреть файл

@ -1,4 +1,5 @@
Write-Host `n"Starting clean up script" -ForegroundColor Green
$var = (Get-Content -Path .\Config\toolkit.subscription.json) | ConvertFrom-Json
$var.Comments = "Cleaned up from deployment"
$var.SubscriptionId = "000000-000-0000-0000"
@ -10,6 +11,10 @@ $vdc = (Get-Content -Path .\Environments\_Common\subscriptions.json) | Con
$vdc.VDCVDI.SubscriptionId = "000000-000-0000-0000"
$vdc.VDCVDI.TenantId = "000000-000-0000-0000"
$vdc | ConvertTo-Json | Set-Content -Path .\Environments\_Common\subscriptions.json
$spoke = (Get-Content -Path .\Environments\_Common\subscriptions.json) | ConvertFrom-Json
$spoke.SPOKE.SubscriptionId = "000000-000-0000-0000"
$spoke.SPOKE.TenantId = "000000-000-0000-0000"
$spoke | ConvertTo-Json | Set-Content -Path .\Environments\_Common\subscriptions.json
$SS = (Get-Content -Path .\Environments\_Common\subscriptions.json) | ConvertFrom-Json
$SS.SharedServices.SubscriptionId = "000000-000-0000-0000"
$SS.SharedServices.TenantId ="000000-000-0000-0000"
@ -22,3 +27,4 @@ $onprem = (Get-Content -Path .\Environments\_Common\subscriptions.json) | 
$onprem.OnPremises.SubscriptionId = "000000-000-0000-0000"
$onprem.OnPremises.TenantId = "000000-000-0000-0000"
$onprem | ConvertTo-Json | Set-Content -Path .\Environments\_Common\subscriptions.json
Write-Host `n"Clean up script was complete. Config files were cleaned up." -ForegroundColor Cyan

Просмотреть файл

@ -819,13 +819,12 @@ Function Get-AllModules {
$topologicalSortRootPath = `
Join-Path $rootPath -ChildPath 'TopologicalSort';
# Adding Out-Null to prevent outputs from the Invoke-Command from being added to
Invoke-Command -ScriptBlock { dotnet build $topologicalSortRootPath --configuration Release --output ./ } | Out-Null
$topologicalSortAssemblyPath = Join-Path $topologicalSortRootPath "TopologicalSort.dll"
Add-Type -Path $topologicalSortAssemblyPath
$graph = [VDC.Core.DirectedGraph]::new()

Просмотреть файл

@ -1,4 +1,8 @@
##### Replace values with environment variables for the toolkit.subscription.json file
Write-Host `n"Running the Pre-Req script for the VDC toolkit" -ForegroundColor Cyan
Write-Host `n"Setting Subscription ID and Tenant ID in the Config Files." -ForegroundColor Green
##### Replace values with environment variables for the toolkit.subscription.json file
$var = (Get-Content -Path .\Config\toolkit.subscription.json) | ConvertFrom-Json
$var.Comments = "ToolKit for creating a new Virtual Data Center"
$var.SubscriptionId = $ENV:SUBSCRIPTION_ID
@ -13,21 +17,27 @@ $vdc.VDCVDI.TenantId = $ENV:TENANT_ID
$vdc | ConvertTo-Json | Set-Content -Path .\Environments\_Common\subscriptions.json
$SS = (Get-Content -Path .\Environments\_Common\subscriptions.json) | ConvertFrom-Json
$SS.SharedServices.SubscriptionId = $ENV:SUBSCRIPTION_ID
$SS.SharedServices.SubscriptionId = $ENV:HUB_SUB_ID
$SS.SharedServices.TenantId = $ENV:TENANT_ID
$SS | ConvertTo-Json | Set-Content -Path .\Environments\_Common\subscriptions.json
$arti = (Get-Content -Path .\Environments\_Common\subscriptions.json) | ConvertFrom-Json
$arti.Artifacts.SubscriptionId = $ENV:SUBSCRIPTION_ID
$arti.Artifacts.SubscriptionId = $ENV:HUB_SUB_ID
$arti.Artifacts.TenantId = $ENV:TENANT_ID
$arti | ConvertTo-Json | Set-Content -Path .\Environments\_Common\subscriptions.json
$arti = (Get-Content -Path .\Environments\_Common\subscriptions.json) | ConvertFrom-Json
$arti.SPOKE.SubscriptionId = $ENV:SUBSCRIPTION_ID
$arti.SPOKE.TenantId = $ENV:TENANT_ID
$arti | ConvertTo-Json | Set-Content -Path .\Environments\_Common\subscriptions.json
$onprem = (Get-Content -Path .\Environments\_Common\subscriptions.json) | ConvertFrom-Json
$onprem.OnPremises.SubscriptionId = $ENV:SUBSCRIPTION_ID
$onprem.OnPremises.TenantId = $ENV:TENANT_ID
$onprem | ConvertTo-Json | Set-Content -Path .\Environments\_Common\subscriptions.json
Write-Host `n"Checking password randomization" -ForegroundColor Green
#### Check if random passwords are needed or if passwords are provided for the VM admin accounts and the Active Directory Account
# Random Password Function
@ -50,10 +60,17 @@ function Get-RandomPassword {
### Check the VM password
if (($null -eq $ENV:ADMIN_USER_PWD) -or ("" -eq $ENV:ADMIN_USER_PWD) -or ("Random" -eq $ENV:ADMIN_USER_PWD) ) {
$ENV:ADMIN_USER_PWD = Get-RandomPassword
Write-Host `n"Admin Password set for random" -ForegroundColor Green
}
else {
Write-Host `n"Admin password was not randomized." -ForegroundColor Cyan
}
### Check the Active Directory (Domain Password)
if (($null -eq $ENV:DOMAIN_ADMIN_USER_PWD) -or ("" -eq $ENV:DOMAIN_ADMIN_USER_PWD) -or ("Random" -eq $ENV:DOMAIN_ADMIN_USER_PWD) ) {
$ENV:DOMAIN_ADMIN_USER_PWD = Get-RandomPassword
Write-Host `n"Domain Password set for random" -ForegroundColor Green
}
else{
Write-Host `n"Domain Password was not randomized." -ForegroundColor Cyan
}

Просмотреть файл

@ -9,7 +9,8 @@ It encourages the use of [modern devops principles](Docs/design-principles.md).
## Documentation
- The easiest way to get started with the toolkit is to follow our [quickstart guide](Docs/quickstart.md).
- Checkout the [latest release notes](Docs/Release/2020-04.md).
- Checkout the [latest release notes](Docs/Release/2019-09.md).
- If you want to utilize the master orchestration script please refer to the following [documentation](Docs/masterOrchestration)
## Repo structure
Here's what is included:

Просмотреть файл

@ -24,9 +24,11 @@ Write-Host "Starting the script for deploying your Shared Services"
Write-Host "The deployment was succesfull if: Exit code $LASTEXITCODE == 0" -Verbose
## Enter the main script for teardown shared services
Write-Host "Starting the script for tearing down Shared Services"
./Orchestration/OrchestrationService/ModuleConfigurationDeployment.ps1 -TearDownEnvironment -DefinitionPath ./Environments/SharedServices/definition.json
Write-Host "Starting the script for deploying MS-VDI"
./Orchestration/OrchestrationService/ModuleConfigurationDeployment.ps1 -DefinitionPath ./Environments/MS-VDI/definition.json
Write-Host "The deployment was succesfull if: Exit code $LASTEXITCODE == 0" -Verbose
## Run the cleanup script so that no values are retained in code for the config files
Write-Host "Executing the cleanup script"

Двоичные данные
images/inputFile_line_change.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 79 KiB

Двоичные данные
images/input_file_ex.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 18 KiB

Двоичные данные
images/input_file_ex2.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 69 KiB

Двоичные данные
images/input_file_ex3.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 39 KiB

Двоичные данные
images/master_script_ex.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 16 KiB

Двоичные данные
images/orchestration_enable.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 81 KiB

Двоичные данные
images/storage_account_ex1.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 36 KiB

Двоичные данные
images/storage_account_ex2.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 80 KiB

49
inputFile.json Normal file
Просмотреть файл

@ -0,0 +1,49 @@
{
"Comments": "This is the Input file for your VDC toolkit orchestration",
"TenantID": "000-000-000",
"azureEnvironmentName": "AzureUSGovernment",
"organizationName": "contoso",
"azureDiscoveryURL": "https://management.azure.com/metadata/endpoints?api-version=2019-05-01",
"azureSentinel": "True",
"SharedServices": [
{
"Hub1": {
"Comments": "First Shared Services ",
"SubscriptionID": "111-111-111",
"location": "USGov Virginia",
"keyVaultObjectID": "11-111-11",
"devOpsID": "1-1111-1",
"adminSSHPubKey": "ssh-RSA 111",
"vmAdminUserName": "Allscript",
"folderName": "SharedServices"
}
}
],
"MSVDI": [
{
"MSVDI1": {
"Comments": "First MS-VDI",
"SubscriptionID": "333-333-333",
"location": "USGov Virginia",
"keyVaultObjectID": "33-333-33",
"devOpsID": "3-333-3",
"adminSSHPubKey": "ssh-RSA 333",
"vmAdminUserName": "Allscript",
"folderName": "MS-VDI"
}
},
{
"MSVDI2": {
"Comments": "Second MS-VDI",
"SubscriptionID": "444-444-444",
"location": "USGov Virginia",
"keyVaultObjectID": "44-444-44",
"devOpsID": "4-444-4",
"adminSSHPubKey": "ssh-RSA 444",
"vmAdminUserName": "Allscript",
"folderName": "MS-VDI-2"
}
}
]
}