From dca18e6cb5d73b857707275dd5ba58142b7d5913 Mon Sep 17 00:00:00 2001 From: Daniel Ciborowski Date: Fri, 25 Mar 2022 23:48:17 -0400 Subject: [PATCH 01/71] Create README.md --- modules/ml-synapse/README.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 modules/ml-synapse/README.md diff --git a/modules/ml-synapse/README.md b/modules/ml-synapse/README.md new file mode 100644 index 000000000000..67b79540e509 --- /dev/null +++ b/modules/ml-synapse/README.md @@ -0,0 +1,16 @@ +# Blank Template + +![Azure Public Test Date](https://azurequickstartsservice.blob.core.windows.net/badges/module/ml-synpase/PublicLastTestDate.svg) +![Azure Public Test Result](https://azurequickstartsservice.blob.core.windows.net/badges/module/ml-synpase/PublicDeployment.svg) + +![Azure US Gov Last Test Date](https://azurequickstartsservice.blob.core.windows.net/badges/module/ml-synpase/FairfaxLastTestDate.svg) +![Azure US Gov Last Test Result](https://azurequickstartsservice.blob.core.windows.net/badges/module/ml-synpase/FairfaxDeployment.svg) + +![Best Practice Check](https://azurequickstartsservice.blob.core.windows.net/badges/module/ml-synpase/BestPracticeResult.svg) +![Cred Scan Check](https://azurequickstartsservice.blob.core.windows.net/badges/module/ml-synpase/CredScanResult.svg) + +[![Deploy To Azure](https://raw.githubusercontent.com/Azure/azure-quickstart-templates/master/1-CONTRIBUTION-GUIDE/images/deploytoazure.svg?sanitize=true)](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2FAzure%2Fazure-quickstart-templates%2Fmaster%2Fmodule%2Fml-synpase%2Fazuredeploy.json) +[![Deploy To Azure US Gov](https://raw.githubusercontent.com/Azure/azure-quickstart-templates/master/1-CONTRIBUTION-GUIDE/images/deploytoazuregov.svg?sanitize=true)](https://portal.azure.us/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2FAzure%2Fazure-quickstart-templates%2Fmaster%2Fmodule%2Fml-synpase%2Fazuredeploy.json) +[![Visualize](https://raw.githubusercontent.com/Azure/azure-quickstart-templates/master/1-CONTRIBUTION-GUIDE/images/visualizebutton.svg?sanitize=true)](http://armviz.io/#/?load=https%3A%2F%2Fraw.githubusercontent.com%2FAzure%2Fazure-quickstart-templates%2Fmaster%2Fmodule%2Fml-synpase%2Fazuredeploy.json) + +`Tags: empty, blank` From debc0c3fd05e4912d7774bc513b2845b55b2178e Mon Sep 17 00:00:00 2001 From: Daniel Ciborowski Date: Fri, 25 Mar 2022 23:52:17 -0400 Subject: [PATCH 02/71] Create main.bicep --- modules/ml-synapse/main.bicep | 167 ++++++++++++++++++++++++++++++++++ 1 file changed, 167 insertions(+) create mode 100644 modules/ml-synapse/main.bicep diff --git a/modules/ml-synapse/main.bicep b/modules/ml-synapse/main.bicep new file mode 100644 index 000000000000..3f49a604e255 --- /dev/null +++ b/modules/ml-synapse/main.bicep @@ -0,0 +1,167 @@ +param name string = newGuid() +param location string +param tags object = { + 'aia-industry': 'industry' + 'aia-solution ': 'solution' + version: '0.0.0' +} + +param defaultDataLakeStorageFilesystemName string = 'workspace' +param storageLocation string = location +param cmkUri string = '' +param storageAccountName string = uniqueString(resourceGroup().id, name) +param azureMLName string = 'aml${uniqueString(resourceGroup().id, name)}' +param appInsights string = 'appin${uniqueString(resourceGroup().id, name)}' +param keyVault string = 'kv${uniqueString(resourceGroup().id, name)}' + +var azureMLSku = 'basic' +var kind = 'StorageV2' +var storageBlobDataContributorRoleID = 'ba92f5b4-2d11-453d-a403-e96b0029c9fe' +var defaultDataLakeStorageAccountName = uniqueString(resourceGroup().id, name) +var defaultDataLakeStorageAccountUrl = 'https://${defaultDataLakeStorageAccountName}.dfs.${environment().suffixes.storage}' +var cmkUriStripVersion = (empty(cmkUri) ? '' : substring(cmkUri, 0, lastIndexOf(cmkUri, '/'))) +var withCmk = { + cmk: { + key: { + name: 'default' + keyVaultUrl: cmkUriStripVersion + } + } +} +var encryption = (empty(cmkUri) ? json('{}') : withCmk) + +resource storageAccount 'Microsoft.Storage/storageAccounts@2019-04-01' = { + name: storageAccountName + location: location + tags: { + Type: 'Synapse Data Lake Storage' + 'Created with': 'Synapse Azure Resource Manager deployment template' + } + sku: { + name: 'Standard_RAGRS' + } + kind: kind + properties: { + isHnsEnabled: true + networkAcls: { + bypass: 'AzureServices' + defaultAction: 'Allow' + } + supportsHttpsTrafficOnly: true + encryption: { + services: { + file: { + enabled: true + } + blob: { + enabled: true + } + } + keySource: 'Microsoft.Storage' + } + accessTier: 'Hot' + } +} + +resource blob 'Microsoft.Storage/storageAccounts/blobServices@2019-04-01' = { + parent: storageAccount + name: 'default' + properties: { + deleteRetentionPolicy: { + enabled: false + } + } +} + +resource container 'Microsoft.Storage/storageAccounts/blobServices/containers@2019-04-01' = { + parent: blob + name: 'workspace' + properties: { + publicAccess: 'None' + } +} + +resource synapseWorkspace 'Microsoft.Synapse/workspaces@2019-06-01-preview' = { + name: name + location: location + tags: tags + identity: { + type: 'SystemAssigned' + } + properties: { + defaultDataLakeStorage: { + accountUrl: defaultDataLakeStorageAccountUrl + filesystem: defaultDataLakeStorageFilesystemName + } + encryption: encryption + } + dependsOn: [ + blob + storageAccount + ] +} + +resource firewallRules 'Microsoft.Synapse/workspaces/firewallrules@2019-06-01-preview' = { + parent: synapseWorkspace + name: 'allowAll' + properties: { + startIpAddress: '0.0.0.0' + endIpAddress: '255.255.255.255' + } +} + +resource roleAssignments 'Microsoft.Storage/storageAccounts/blobServices/containers/providers/roleAssignments@2018-09-01-preview' = { + name: '${defaultDataLakeStorageAccountName}/default/${defaultDataLakeStorageFilesystemName}/Microsoft.Authorization/${guid('${resourceGroup().id}/${storageBlobDataContributorRoleID}/storageRoleDeploymentResource')}' + location: storageLocation + properties: { + roleDefinitionId: resourceId('Microsoft.Authorization/roleDefinitions', storageBlobDataContributorRoleID) + principalId: reference('Microsoft.Synapse/workspaces/${name}', '2019-06-01-preview', 'Full').identity.principalId + principalType: 'ServicePrincipal' + } +} + +resource mlKeyVault 'Microsoft.KeyVault/vaults@2019-09-01' = { + tags: tags + name: keyVault + location: location + properties: { + accessPolicies: [] + enableSoftDelete: true + softDeleteRetentionInDays: 7 + sku: { + family: 'A' + name: 'standard' + } + tenantId: subscription().tenantId + } +} + +resource mlAppInsights 'Microsoft.Insights/components@2020-02-02-preview' = { + tags: tags + name: appInsights + location: (((location == 'eastus2') || (location == 'westcentralus')) ? 'southcentralus' : location) + kind: 'web' + properties: { + Application_Type: 'web' + } +} + +resource azureML 'Microsoft.MachineLearningServices/workspaces@2020-06-01' = { + tags: tags + name: azureMLName + location: location + sku: { + name: azureMLSku + tier: azureMLSku + } + properties: { + applicationInsights: mlAppInsights.id + friendlyName: azureMLName + keyVault: mlKeyVault.id + storageAccount: storageAccount.id + // hbiWorkspace: hbiWorkspace + } + identity: { + type: 'SystemAssigned' + } +} From 74d45141ff64717a3b443dd8a0c27eebfff0e097 Mon Sep 17 00:00:00 2001 From: Daniel Ciborowski Date: Fri, 25 Mar 2022 23:53:09 -0400 Subject: [PATCH 03/71] azuredeploy.parameters.json --- modules/ml-synapse/azuredeploy.parameters.json | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 modules/ml-synapse/azuredeploy.parameters.json diff --git a/modules/ml-synapse/azuredeploy.parameters.json b/modules/ml-synapse/azuredeploy.parameters.json new file mode 100644 index 000000000000..489a20a5769f --- /dev/null +++ b/modules/ml-synapse/azuredeploy.parameters.json @@ -0,0 +1,15 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "_artifactsLocation": { + "value": "" + }, + "_artifactsLocationSasToken": { + "value": "" + }, + "location": { + "value": "eastus" + } + } +} From 35c75531f6c91f087eab657bf360adf6cf4ddb79 Mon Sep 17 00:00:00 2001 From: Daniel Ciborowski Date: Fri, 25 Mar 2022 23:56:15 -0400 Subject: [PATCH 04/71] Create metadata.json --- modules/ml-synapse/metadata.json | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 modules/ml-synapse/metadata.json diff --git a/modules/ml-synapse/metadata.json b/modules/ml-synapse/metadata.json new file mode 100644 index 000000000000..e62dccb8f7ab --- /dev/null +++ b/modules/ml-synapse/metadata.json @@ -0,0 +1,9 @@ +{ + "$schema": "https://aka.ms/azure-quickstart-templates-metadata-schema#", + "type": "Module", + "itemDisplayName": "Machine Learning with Synapse Workspace", + "description": "Azure Machine Learning and Synapse deployed with interconnected resources.", + "summary": "This template deploys an Azure Machine Learning, and Synapse Workspace. The common resources required for both are configured to connect and share common resources, such as the storage account and key vault", + "githubUsername": "dciborow", + "dateUpdated": "2022-03-25" +} From 0f59a4326447438ccce86fa447ca4a5e3262e7bb Mon Sep 17 00:00:00 2001 From: Daniel Ciborowski Date: Sat, 26 Mar 2022 00:02:53 -0400 Subject: [PATCH 05/71] Create azuredeploy.json --- modules/ml-synapse/azuredeploy.json | 231 ++++++++++++++++++++++++++++ 1 file changed, 231 insertions(+) create mode 100644 modules/ml-synapse/azuredeploy.json diff --git a/modules/ml-synapse/azuredeploy.json b/modules/ml-synapse/azuredeploy.json new file mode 100644 index 000000000000..ae6de9a4f2a9 --- /dev/null +++ b/modules/ml-synapse/azuredeploy.json @@ -0,0 +1,231 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.4.1318.3566", + "templateHash": "16992569491940042489" + } + }, + "parameters": { + "name": { + "type": "string", + "defaultValue": "[newGuid()]" + }, + "location": { + "type": "string" + }, + "tags": { + "type": "object", + "defaultValue": { + "aia-industry": "industry", + "aia-solution ": "solution", + "version": "0.0.0" + } + }, + "defaultDataLakeStorageFilesystemName": { + "type": "string", + "defaultValue": "workspace" + }, + "storageLocation": { + "type": "string", + "defaultValue": "[parameters('location')]" + }, + "cmkUri": { + "type": "string", + "defaultValue": "" + }, + "storageAccountName": { + "type": "string", + "defaultValue": "[uniqueString(resourceGroup().id, parameters('name'))]" + }, + "azureMLName": { + "type": "string", + "defaultValue": "[format('aml{0}', uniqueString(resourceGroup().id, parameters('name')))]" + }, + "appInsights": { + "type": "string", + "defaultValue": "[format('appin{0}', uniqueString(resourceGroup().id, parameters('name')))]" + }, + "keyVault": { + "type": "string", + "defaultValue": "[format('kv{0}', uniqueString(resourceGroup().id, parameters('name')))]" + } + }, + "variables": { + "azureMLSku": "basic", + "kind": "StorageV2", + "storageBlobDataContributorRoleID": "ba92f5b4-2d11-453d-a403-e96b0029c9fe", + "defaultDataLakeStorageAccountName": "[uniqueString(resourceGroup().id, parameters('name'))]", + "defaultDataLakeStorageAccountUrl": "[format('https://{0}.dfs.{1}', variables('defaultDataLakeStorageAccountName'), environment().suffixes.storage)]", + "cmkUriStripVersion": "[if(empty(parameters('cmkUri')), '', substring(parameters('cmkUri'), 0, lastIndexOf(parameters('cmkUri'), '/')))]", + "withCmk": { + "cmk": { + "key": { + "name": "default", + "keyVaultUrl": "[variables('cmkUriStripVersion')]" + } + } + }, + "encryption": "[if(empty(parameters('cmkUri')), json('{}'), variables('withCmk'))]" + }, + "resources": [ + { + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2019-04-01", + "name": "[parameters('storageAccountName')]", + "location": "[parameters('location')]", + "tags": { + "Type": "Synapse Data Lake Storage", + "Created with": "Synapse Azure Resource Manager deployment template" + }, + "sku": { + "name": "Standard_RAGRS" + }, + "kind": "[variables('kind')]", + "properties": { + "isHnsEnabled": true, + "networkAcls": { + "bypass": "AzureServices", + "defaultAction": "Allow" + }, + "supportsHttpsTrafficOnly": true, + "encryption": { + "services": { + "file": { + "enabled": true + }, + "blob": { + "enabled": true + } + }, + "keySource": "Microsoft.Storage" + }, + "accessTier": "Hot" + } + }, + { + "type": "Microsoft.Storage/storageAccounts/blobServices", + "apiVersion": "2019-04-01", + "name": "[format('{0}/{1}', parameters('storageAccountName'), 'default')]", + "properties": { + "deleteRetentionPolicy": { + "enabled": false + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName'))]" + ] + }, + { + "type": "Microsoft.Storage/storageAccounts/blobServices/containers", + "apiVersion": "2019-04-01", + "name": "[format('{0}/{1}/{2}', parameters('storageAccountName'), 'default', 'workspace')]", + "properties": { + "publicAccess": "None" + }, + "dependsOn": [ + "[resourceId('Microsoft.Storage/storageAccounts/blobServices', parameters('storageAccountName'), 'default')]", + "[resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName'))]" + ] + }, + { + "type": "Microsoft.Synapse/workspaces", + "apiVersion": "2019-06-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "identity": { + "type": "SystemAssigned" + }, + "properties": { + "defaultDataLakeStorage": { + "accountUrl": "[variables('defaultDataLakeStorageAccountUrl')]", + "filesystem": "[parameters('defaultDataLakeStorageFilesystemName')]" + }, + "encryption": "[variables('encryption')]" + }, + "dependsOn": [ + "[resourceId('Microsoft.Storage/storageAccounts/blobServices', parameters('storageAccountName'), 'default')]", + "[resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName'))]" + ] + }, + { + "type": "Microsoft.Synapse/workspaces/firewallRules", + "apiVersion": "2019-06-01-preview", + "name": "[format('{0}/{1}', parameters('name'), 'allowAll')]", + "properties": { + "startIpAddress": "0.0.0.0", + "endIpAddress": "255.255.255.255" + }, + "dependsOn": [ + "[resourceId('Microsoft.Synapse/workspaces', parameters('name'))]" + ] + }, + { + "type": "Microsoft.Storage/storageAccounts/blobServices/containers/providers/roleAssignments", + "apiVersion": "2018-09-01-preview", + "name": "[format('{0}/default/{1}/Microsoft.Authorization/{2}', variables('defaultDataLakeStorageAccountName'), parameters('defaultDataLakeStorageFilesystemName'), guid(format('{0}/{1}/storageRoleDeploymentResource', resourceGroup().id, variables('storageBlobDataContributorRoleID'))))]", + "location": "[parameters('storageLocation')]", + "properties": { + "roleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', variables('storageBlobDataContributorRoleID'))]", + "principalId": "[reference(format('Microsoft.Synapse/workspaces/{0}', parameters('name')), '2019-06-01-preview', 'Full').identity.principalId]", + "principalType": "ServicePrincipal" + } + }, + { + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2019-09-01", + "name": "[parameters('keyVault')]", + "tags": "[parameters('tags')]", + "location": "[parameters('location')]", + "properties": { + "accessPolicies": [], + "enableSoftDelete": true, + "softDeleteRetentionInDays": 7, + "sku": { + "family": "A", + "name": "standard" + }, + "tenantId": "[subscription().tenantId]" + } + }, + { + "type": "Microsoft.Insights/components", + "apiVersion": "2020-02-02-preview", + "name": "[parameters('appInsights')]", + "tags": "[parameters('tags')]", + "location": "[if(or(equals(parameters('location'), 'eastus2'), equals(parameters('location'), 'westcentralus')), 'southcentralus', parameters('location'))]", + "kind": "web", + "properties": { + "Application_Type": "web" + } + }, + { + "type": "Microsoft.MachineLearningServices/workspaces", + "apiVersion": "2020-06-01", + "name": "[parameters('azureMLName')]", + "tags": "[parameters('tags')]", + "location": "[parameters('location')]", + "sku": { + "name": "[variables('azureMLSku')]", + "tier": "[variables('azureMLSku')]" + }, + "properties": { + "applicationInsights": "[resourceId('Microsoft.Insights/components', parameters('appInsights'))]", + "friendlyName": "[parameters('azureMLName')]", + "keyVault": "[resourceId('Microsoft.KeyVault/vaults', parameters('keyVault'))]", + "storageAccount": "[resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName'))]" + }, + "identity": { + "type": "SystemAssigned" + }, + "dependsOn": [ + "[resourceId('Microsoft.Insights/components', parameters('appInsights'))]", + "[resourceId('Microsoft.KeyVault/vaults', parameters('keyVault'))]", + "[resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName'))]" + ] + } + ] +} From b2ddefcbbb5ff560b823e90504eb638fb0b757a2 Mon Sep 17 00:00:00 2001 From: Daniel Ciborowski Date: Fri, 1 Apr 2022 18:00:47 -0400 Subject: [PATCH 06/71] Create README.md --- application-workloads/azure-gamedev/gamedev-vm/README.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 application-workloads/azure-gamedev/gamedev-vm/README.md diff --git a/application-workloads/azure-gamedev/gamedev-vm/README.md b/application-workloads/azure-gamedev/gamedev-vm/README.md new file mode 100644 index 000000000000..8b137891791f --- /dev/null +++ b/application-workloads/azure-gamedev/gamedev-vm/README.md @@ -0,0 +1 @@ + From 05c70887f4ce7d5bf6ded4b059b9fa943c52e716 Mon Sep 17 00:00:00 2001 From: Daniel Ciborowski Date: Fri, 1 Apr 2022 18:02:04 -0400 Subject: [PATCH 07/71] Create metadata.json --- .../azure-gamedev/gamedev-vm/metadata.json | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 application-workloads/azure-gamedev/gamedev-vm/metadata.json diff --git a/application-workloads/azure-gamedev/gamedev-vm/metadata.json b/application-workloads/azure-gamedev/gamedev-vm/metadata.json new file mode 100644 index 000000000000..a3d3795897d7 --- /dev/null +++ b/application-workloads/azure-gamedev/gamedev-vm/metadata.json @@ -0,0 +1,9 @@ +{ + "$schema": "https://aka.ms/azure-quickstart-templates-metadata-schema#", + "type": "Module", + "itemDisplayName": "Azure Game Developer Virtual Machine", + "description": "Azure Game Developer Virtual Machine includes Licencsed Engines like Unreal.", + "summary": "This template deploys an Azure Game Developer Virtual Machine.", + "githubUsername": "dciborow", + "dateUpdated": "2022-04-01" +} From 498e4b9046d7f5d0acd3d329f803bb3e789a2039 Mon Sep 17 00:00:00 2001 From: Daniel Ciborowski Date: Fri, 1 Apr 2022 18:02:28 -0400 Subject: [PATCH 08/71] Create main.bicep --- .../azure-gamedev/gamedev-vm/main.bicep | 105 ++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 application-workloads/azure-gamedev/gamedev-vm/main.bicep diff --git a/application-workloads/azure-gamedev/gamedev-vm/main.bicep b/application-workloads/azure-gamedev/gamedev-vm/main.bicep new file mode 100644 index 000000000000..75404fffa726 --- /dev/null +++ b/application-workloads/azure-gamedev/gamedev-vm/main.bicep @@ -0,0 +1,105 @@ +/* Copyright (c) Microsoft Corporation. All rights reserved. + This Template deploys a Industrial AI Application +*/ + +/* Application Selection + Each application must provide a Plan and a Parameter Mapping. + */ + param apps object = { + gamingDevVM : gamingDevVM + } + /* Application Plans + Each Managed Application requires a plan object. This may be overriden as a parameter input. + */ + param gamingVMPlan object = { + name : 'gaming-dev-vm-ama' + product : 'f39e77ad-ada5-4145-9291-643e6e3ce1b2' + publisher: publisher + version : '0.1.4' + } + /* Application Parameter Mapping + Each Managed Application requires a parameter mapping. + */ + param gamingDevVM object = { + plan: gamingVMPlan + parameters: { + location: { + value: location + } + vmSize: { + value: 'Standard_NV12s_v3' + } + adminName: { + value: administratorLogin + } + adminPass: { + value: passwordAdministratorLogin + } + osType: { + value: osType + } + gameEngine: { + value: gameEngine + } + } + } +// + +// Parameters + param jitAccessEnabled bool = false + param location string = resourceGroup().location + param publisher string = 'microsoftcorporation1602274591143' // Prod Account + param applicationResourceName string = '611f626e14154c0fb7f29099c2ff95a0' + param managedResourceGroupId string = '' + + // Datastore Parmeters + param storageAccountNewOrExisting string = 'existing' + param storageAccountResourceGroup string = '' + param storageAccount string = '' + param dataContainer string = 'retailidmsampledata' + param cosmosDB string = '' + param dataExplorer string = '' + + // GamingVM Parameters + param gameEngine string = 'ue_4_27' + param osType string = 'win10' + + // Endpoint Parameters + param aksAgentCount int = 3 + + // Discrete Parameters + @allowed([ + 'gamingDevVM' + ]) + param solution string + + @allowed([ + 'true' + 'false' + ]) + param crossTenant string = 'true' + + + // Secure Parameters + @secure() + param servicePrincipalClientID string = '' + + @secure() + param servicePrincipalSecret string = '' +// + +// Resources + resource solution_resource 'Microsoft.Solutions/applications@2017-09-01' = { + name : solution + location: location + kind : 'MarketPlace' + plan : apps[solution].plan + properties: { + managedResourceGroupId: (empty(managedResourceGroupId) ? '${subscription().id}/resourceGroups/${take('${resourceGroup().name}-mrg', 90)}' : managedResourceGroupId) + parameters : apps[solution].parameters + jitAccessPolicy : { + jitAccessEnabled : jitAccessEnabled + } + } + } +// From ee55353a58ca17f84a66933ff105734579b670da Mon Sep 17 00:00:00 2001 From: Daniel Ciborowski Date: Fri, 1 Apr 2022 18:04:07 -0400 Subject: [PATCH 09/71] Create azuredeploy.parameters.json --- .../gamedev-vm/azuredeploy.parameters.json | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 application-workloads/azure-gamedev/gamedev-vm/azuredeploy.parameters.json diff --git a/application-workloads/azure-gamedev/gamedev-vm/azuredeploy.parameters.json b/application-workloads/azure-gamedev/gamedev-vm/azuredeploy.parameters.json new file mode 100644 index 000000000000..31e7ab00d6bc --- /dev/null +++ b/application-workloads/azure-gamedev/gamedev-vm/azuredeploy.parameters.json @@ -0,0 +1,26 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "solution": { + "value": "gamingDevVM" + }, + "location": { + "value": "eastus" + }, + "administratorLogin": { + "value": "adaccount" + }, + "passwordAdministratorLogin": { + "value": "B1gD6taRocks!B1gD6taRocks&" + }, + "gamingVMPlan": { + "value": { + "name": "game-dev-vm-amatest-plan", + "product": "f39e77ad-ada5-4145-9291-643e6e3ce1b2", + "publisher": "microsoftcorporation1602274591143", + "version": "**PACKAGE_VERSION**" + } + } + } +} From 785f747dc5b8540fa3b12efcc9d758e825846ea6 Mon Sep 17 00:00:00 2001 From: Daniel Ciborowski Date: Mon, 4 Apr 2022 16:09:09 -0400 Subject: [PATCH 10/71] adding Solution Template --- .../gamedev-vm/MountFIleShare.ps1 | 12 + .../gamedev-vm/TeradiciRegCAS.ps1 | 96 ++++ .../gamedev-vm/azuredeploy.parameters.json | 26 -- .../gamedev-vm/createDataDisk.ps1 | 19 + .../azure-gamedev/gamedev-vm/gamedev-vm.bicep | 409 ++++++++++++++++++ .../azure-gamedev/gamedev-vm/ibSetup.ps1 | 77 ++++ .../azure-gamedev/gamedev-vm/main.bicep | 166 +++---- .../azure-gamedev/gamedev-vm/main_st.bicep | 52 +++ .../azure-gamedev/gamedev-vm/nsgRules.bicep | 239 ++++++++++ .../azure-gamedev/gamedev-vm/p4DepotSync.ps1 | 84 ++++ .../gamedev-vm/remoteAccessExtension.bicep | 80 ++++ 11 files changed, 1138 insertions(+), 122 deletions(-) create mode 100644 application-workloads/azure-gamedev/gamedev-vm/MountFIleShare.ps1 create mode 100644 application-workloads/azure-gamedev/gamedev-vm/TeradiciRegCAS.ps1 delete mode 100644 application-workloads/azure-gamedev/gamedev-vm/azuredeploy.parameters.json create mode 100644 application-workloads/azure-gamedev/gamedev-vm/createDataDisk.ps1 create mode 100644 application-workloads/azure-gamedev/gamedev-vm/gamedev-vm.bicep create mode 100644 application-workloads/azure-gamedev/gamedev-vm/ibSetup.ps1 create mode 100644 application-workloads/azure-gamedev/gamedev-vm/main_st.bicep create mode 100644 application-workloads/azure-gamedev/gamedev-vm/nsgRules.bicep create mode 100644 application-workloads/azure-gamedev/gamedev-vm/p4DepotSync.ps1 create mode 100644 application-workloads/azure-gamedev/gamedev-vm/remoteAccessExtension.bicep diff --git a/application-workloads/azure-gamedev/gamedev-vm/MountFIleShare.ps1 b/application-workloads/azure-gamedev/gamedev-vm/MountFIleShare.ps1 new file mode 100644 index 000000000000..fc858a5d20de --- /dev/null +++ b/application-workloads/azure-gamedev/gamedev-vm/MountFIleShare.ps1 @@ -0,0 +1,12 @@ +param ( + [string]$storageAccount, + [string]$storageAccountKey, + [string]$fileShareName) + +if ($storageAccount -and $storageAccountKey -and $fileShareName) +{ + Add-Content 'C:\\ProgramData\\Microsoft\\Windows\\Start Menu\\Programs\\Startup\\mountfileshare.cmd' "@ECHO OFF`r`necho Mounting Azure Storage File Share" + Add-Content 'C:\\ProgramData\\Microsoft\\Windows\\Start Menu\\Programs\\Startup\\mountfileshare.cmd' "cmdkey /add:$storageAccount.file.core.windows.net /user:localhost\$storageAccount /pass:`"$storageAccountKey`"" + Add-Content 'C:\\ProgramData\\Microsoft\\Windows\\Start Menu\\Programs\\Startup\\mountfileshare.cmd' "net use S: `"\\$storageAccount.file.core.windows.net\$fileShareName`" /persistent:yes" + Add-Content 'C:\\ProgramData\\Microsoft\\Windows\\Start Menu\\Programs\\Startup\\mountfileshare.cmd' "del `"C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Startup\mountfileshare.cmd`"" +} \ No newline at end of file diff --git a/application-workloads/azure-gamedev/gamedev-vm/TeradiciRegCAS.ps1 b/application-workloads/azure-gamedev/gamedev-vm/TeradiciRegCAS.ps1 new file mode 100644 index 000000000000..9c91d42feb23 --- /dev/null +++ b/application-workloads/azure-gamedev/gamedev-vm/TeradiciRegCAS.ps1 @@ -0,0 +1,96 @@ + +# Copyright (c) 2021 Teradici Corporation +# +# This source code is licensed under the MIT license found in the +# LICENSE file in the root directory of this source tree. +[CmdletBinding(DefaultParameterSetName = "_AllParameterSets")] +param( + [Parameter(Mandatory = $true)] + [string] + $pcoip_registration_code +) +$InstalledAgentPath = 'C:\Program Files\Teradici\PCoIP Agent\' +$destPath= "C:\Teradici\" +$LOG_FILE = $destPath +'CSELog.txt' + +$global:restart = $false + +if (!(Test-Path $destPath)) +{ + try { New-Item -Path $destPath -ItemType Directory -ErrorAction Stop } + catch { Write-Error -Message "Unable to Create Directory: $destPath " } +} +function Log([string] $String){ + $TimeStamp="[{0:dd-MMM-yy} {0:HH:mm:ss}]" -f (Get-Date) + Write-Output "$TimeStamp $String" | Out-File -FilePath $LOG_FILE -Append -Force +} + +Log "START----------------------> Input Parameters:" +Log "pcoip_registration_code: $pcoip_registration_code" + +#------------------------------------------------------------------------- +# Retry function, defaults to try 30 times with 10s intervals = 5min +#-------------------------------------------------------------------------- +function Retry([scriptblock]$Action, $Interval = 10, $Attempts = 30) { + $Current_Attempt = 0 + while ($true) { + $Current_Attempt++ + $rc = $Action.Invoke() + + #If successful return Action Invoke return + if ($?) { return $rc } + if ($Current_Attempt -ge $Attempts) { + Log "--> ERROR: Failed after $Current_Attempt attempt(s)." -InformationAction Continue + #throw ??? this would throw off my Try-catch block ... + } + Log "--> Attempt $Current_Attempt failed. Retrying in $Interval seconds..." -InformationAction Continue + Start-Sleep -Seconds $Interval + } +} +function IS_PCoIPAgent_Installed { + $arrService = Get-Service "PCoIPAgent*" + if(($null -eq $arrService)){ + return $false + } + return $true +} + +################################################################# +Log "MAIN ---------------->Custom Script Extension " +Log "---> Script running as user '$(whoami)'." +################################################################# + +try { + if (IS_PCoIPAgent_Installed){ + if(!([string]::IsNullOrEmpty($pcoip_registration_code))){ + + Set-Location $InstalledAgentPath + Log "--> Checking for existing PCoIP License at: $InstalledAgentPath " + & powershell .\pcoip-validate-license.ps1 *>$null + + if ( $LastExitCode -eq 0 ) { + Log "--> Found a valid license" + } + else { + Log "--> Retry action to register PCoIP Agent with register-host.ps1" + Retry -Action { powershell .\pcoip-register-host.ps1 -RegistrationCode $pcoip_registration_code *>$null } + Log "--> Retry Returned " + + if ($LastExitCode -ne 0) { + $errMsg = "Teradici registration failed for the provided code. You can try to register the PCoIP Agent manually by connecting to the VM via RDP and following Teradici's instructions at https://www.teradici.com/web-help/pcoip_agent/standard_agent/windows/2.15.0/admin-guide/licensing/licensing" + throw $errMsg + } + + #Agent Restart + Log "--> PCoIP Agent Restart " + Restart-Service -Name PCoIPAgent + } + + } + } +} +catch [Exception]{ + Log $_.Exception.Message + Add-Content 'C:\Users\Public\Desktop\INSTALLED_SOFTWARE.txt' "ERROR: $($_.Exception.Message)" + throw $_.Exception.Message +} \ No newline at end of file diff --git a/application-workloads/azure-gamedev/gamedev-vm/azuredeploy.parameters.json b/application-workloads/azure-gamedev/gamedev-vm/azuredeploy.parameters.json deleted file mode 100644 index 31e7ab00d6bc..000000000000 --- a/application-workloads/azure-gamedev/gamedev-vm/azuredeploy.parameters.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", - "contentVersion": "1.0.0.0", - "parameters": { - "solution": { - "value": "gamingDevVM" - }, - "location": { - "value": "eastus" - }, - "administratorLogin": { - "value": "adaccount" - }, - "passwordAdministratorLogin": { - "value": "B1gD6taRocks!B1gD6taRocks&" - }, - "gamingVMPlan": { - "value": { - "name": "game-dev-vm-amatest-plan", - "product": "f39e77ad-ada5-4145-9291-643e6e3ce1b2", - "publisher": "microsoftcorporation1602274591143", - "version": "**PACKAGE_VERSION**" - } - } - } -} diff --git a/application-workloads/azure-gamedev/gamedev-vm/createDataDisk.ps1 b/application-workloads/azure-gamedev/gamedev-vm/createDataDisk.ps1 new file mode 100644 index 000000000000..3d6ef434c5f1 --- /dev/null +++ b/application-workloads/azure-gamedev/gamedev-vm/createDataDisk.ps1 @@ -0,0 +1,19 @@ +#$size = (Get-PartitionSupportedSize -DriveLetter 'C') +#Resize-Partition -DriveLetter 'C' -Size $size.SizeMax + +If ((Get-PhysicalDisk).Count -lt 3) { Exit } + +New-StoragePool –FriendlyName LUN-0 –StorageSubsystemFriendlyName 'Windows Storage*' –PhysicalDisks (Get-PhysicalDisk -FriendlyName 'Msft Virtual Disk') + +New-VirtualDisk -FriendlyName DataDisk1 -StoragePoolFriendlyName LUN-0 -UseMaximumSize -ResiliencySettingName Simple +Start-Sleep -Seconds 20 + +Initialize-Disk -VirtualDisk (Get-VirtualDisk -FriendlyName DataDisk1) +Start-Sleep -Seconds 20 + +$diskNumber = ((Get-VirtualDisk -FriendlyName DataDisk1 | Get-Disk).Number) +New-Partition -DiskNumber $diskNumber -UseMaximumSize -AssignDriveLetter +Start-Sleep -Seconds 20 + + +Format-Volume -DriveLetter F -FileSystem NTFS -NewFileSystemLabel Data -Confirm:$false -Force \ No newline at end of file diff --git a/application-workloads/azure-gamedev/gamedev-vm/gamedev-vm.bicep b/application-workloads/azure-gamedev/gamedev-vm/gamedev-vm.bicep new file mode 100644 index 000000000000..24d84deae057 --- /dev/null +++ b/application-workloads/azure-gamedev/gamedev-vm/gamedev-vm.bicep @@ -0,0 +1,409 @@ +@description('Resource Location.') +param location string = resourceGroup().location + +@description('Virtual Machine Size.') +param vmSize string = 'Standard_NV12s_v3' + +@description('Virtual Machine Name.') +param vmName string = 'gamedevvm' + +@description('Virtual Machine User Name .') +param adminName string + +@metadata({ + type: 'password' + description: 'Admin password.' +}) +@secure() +param adminPass string + +@description('Operating System type') +param osType string = 'win10' + +@description('Game Engine') +@allowed([ + 'ue_4_27' + 'ue_5_0ea' + 'unity_2020_3_19f1' +]) +param gameEngine string = 'ue_4_27' + +@description('GDK Version') +param gdkVersion string = 'June_2021_Update_4' + +@description('Incredibuild License Key') +@secure() +param ibLicenseKey string = '' + +@description('Remote Access technology') +@allowed([ + 'RDP' + 'Teradici' + 'Parsec' +]) +param remoteAccessTechnology string = 'RDP' + +@description('Teradici Registration Key') +@secure() +param teradiciRegKey string = '' + +@description('Parsec Team ID') +param parsec_teamId string = '' + +@description('Parsec Team Key') +@secure() +param parsec_teamKey string = '' + +@description('Parsec Hostname') +param parsec_host string = '' + +@description('Parsec User Email') +param parsec_userEmail string = '' + +@description('Parsec Is Guest Access') +param parsec_isGuestAccess bool = false + +@description('Number of data disks') +param numDataDisks int = 0 + +@description('Disk Performance Tier') +param dataDiskSize int = 1024 + +@description('File Share Storage Account name') +param fileShareStorageAccount string = '' + +@description('File Share Storage Account key') +@secure() +param fileShareStorageAccountKey string = '' + +@description('File Share name') +param fileShareName string = '' + +@description('Perforce Port address') +param p4Port string = '' + +@description('Perforce User') +param p4Username string = '' + +@description('Perforce User password') +@secure() +param p4Password string = '' + +@description('Perforce Client Workspace') +param p4Workspace string = '' + +@description('Perforce Stream') +param p4Stream string = '' + +@description('Perforce Depot Client View mappings') +param p4ClientViews string = '' + +@description('Virtual Network name') +param vnetName string = 'gamedev-vnet' + +@description('Address prefix of the virtual network') +param vnetARPrefixes array = [ + '10.0.0.0/26' +] + +@description('Virtual network is new or existing') +param vnetNewOrExisting string = 'new' + +@description('Resource Group of the Virtual network') +param vnetRGName string = resourceGroup().name + +@description('VM Subnet name') +param subNetName string = 'gamedev-vnet-subnet1' + +@description('Subnet prefix of the virtual network') +param subNetARPrefix string = '10.0.0.0/28' + +@description('Unique public ip address name') +param publicIpName string = 'GameDevVM-IP' + +@description('Unique DNS Public IP attached the VM') +param publicIpDns string = 'gamedevvm${uniqueString(resourceGroup().id)}' + +@description('Public IP Allocoation Method') +param publicIpAllocationMethod string = 'Dynamic' + +@description('SKU number') +@allowed([ + 'Basic' + 'Standard' +]) +param publicIpSku string = 'Basic' + +@description('Public IP New or Existing or None?') +param publicIpNewOrExisting string = 'new' + +@description('Resource Group of the Public IP Address') +param publicIpRGName string = resourceGroup().name + +@allowed([ + 'development' + 'production' +]) +param environment string = 'production' + +@description('Tags by resource.') +param outTagsByResource object = {} + +@description('The base URI where artifacts required by this template are located including a trailing \'/\'') +param _artifactsLocation string = deployment().properties.templateLink.uri + +@description('The sasToken required to access _artifactsLocation.') +@secure() +param _artifactsLocationSasToken string = '' + +@description('Enable or disable Unreal Pixel Streaming port.') +param unrealPixelStreamingEnabled bool = false + +param enableManagedIdentity bool = false +param enableAAD bool = false + +var environmentMapping = { + ue_4_27: 'unreal_4_27' + ue_5_0ea: 'unreal_5_0ea' + unity_2020_3_19f1: 'unity_2020_3_19f1' +} + +var environments = { + development: { + vmImage: { + publisher: 'microsoft-agci-gaming' + offer: 'agci-gamedev-image' + sku: 'gamedev-${gameEngine}-${osType}' + version: 'latest' + } + vmPlan: { + publisher: 'microsoft-agci-gaming' + product: 'agci-gamedev-image' + name: 'gamedev-${gameEngine}-${osType}' + } + } + production: { + vmImage: { + publisher: 'microsoftcorporation1602274591143' + offer: 'game-dev-vm' + sku: '${osType}_${environmentMapping[gameEngine]}' + version: 'latest' + } + vmPlan: { + publisher: 'microsoftcorporation1602274591143' + product: 'game-dev-vm' + name: '${osType}_${environmentMapping[gameEngine]}' + } + } +} + +var vmImage = environments[environment].vmImage +var vmPlan = environments[environment].vmPlan + +var vmName_var = vmName +var ipconfName = '${vmName_var}-ipconf' +var nicName_var = '${vmName_var}-nic' +var nsgName_var = '${vmName_var}-nsg' + +var storageType_var = (bool(length(split(vmSize, '_')) > 2) ? 'Premium_LRS' : 'Standard_LRS') + +var tags_var = { + 'solution': 'Game Development Virtual Machine' + 'engine': gameEngine + 'ostype': osType + 'remotesoftware': remoteAccessTechnology +} + +var cmdGDKInstall = '(Get-Content \'C:\\ProgramData\\Microsoft\\Windows\\Start Menu\\Programs\\Startup\\gdkinstall.cmd\').replace(\'[VERSION]\', \'${gdkVersion}\') | Set-Content \'C:\\ProgramData\\Microsoft\\Windows\\Start Menu\\Programs\\Startup\\gdkinstall.cmd\'' +var userData = 'team_id=${parsec_teamId}:key=${parsec_teamKey}:name=${parsec_host}:user_email=${parsec_userEmail}:is_guest_access=${parsec_isGuestAccess}:ibLicenseKey=${ibLicenseKey}' +var Script2Run = 'TeradiciRegCAS.ps1' +var CSEParams = ' -pcoip_registration_code ${teradiciRegKey}' +var cmdTeradiciRegistration = './${Script2Run}${CSEParams}' + +var vnetId = { + 'new' : resourceId('Microsoft.Network/virtualNetworks', vnetName) + 'existing': resourceId(vnetRGName, 'Microsoft.Network/virtualNetworks', vnetName) +} +var subnetId = '${vnetId[vnetNewOrExisting]}/subnets/${subNetName}' + +var publicIpId = { + 'new': resourceId('Microsoft.Network/publicIPAddresses', publicIpName) + 'existing': resourceId(publicIpRGName, 'Microsoft.Network/publicIPAddresses', publicIpName) + 'none': '' +}[publicIpNewOrExisting] + +resource partnercenter 'Microsoft.Resources/deployments@2020-06-01' = { + name: 'pid-7837dd60-4ba8-419a-a26f-237bbe170773-partnercenter' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource publicIp 'Microsoft.Network/publicIPAddresses@2021-03-01' = if (publicIpNewOrExisting == 'new') { + name: publicIpName + sku: { + name: publicIpSku + } + location: location + properties: { + publicIPAllocationMethod: publicIpAllocationMethod + dnsSettings: { + domainNameLabel: publicIpDns + } + } +} + +module nsg_rules 'nsgRules.bicep' = { + name: 'nsg_rules' + params: { + addPixelStreamingPorts: unrealPixelStreamingEnabled + } +} + +resource nsg 'Microsoft.Network/networkSecurityGroups@2021-03-01' = { + name: nsgName_var + location: location + properties: { + securityRules: nsg_rules.outputs.nsgRules['nsgRules-${remoteAccessTechnology}'] + } +} + +resource vnet 'Microsoft.Network/virtualNetworks@2021-03-01' = if (vnetNewOrExisting == 'new') { + name: vnetName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + first(vnetARPrefixes) + ] + } + subnets: [ + { + name: subNetName + properties: { + addressPrefix: subNetARPrefix + } + } + ] + } +} + +resource nic 'Microsoft.Network/networkInterfaces@2021-03-01' = { + name: nicName_var + location: location + dependsOn: [ + vnet + ] + properties: { + enableAcceleratedNetworking: (bool(length(split(vmSize, '_')) > 2) ? true : false) + ipConfigurations: [ + { + name: ipconfName + properties: union( { + subnet: { + id: subnetId + } + }, { + privateIPAllocationMethod: 'Dynamic' + }, (!empty(publicIpId)) ? { + publicIPAddress: { + id: publicIpId + } + } : {} ) + } + ] + networkSecurityGroup: { + id: nsg.id + } + } +} + +resource virtualMachine 'Microsoft.Compute/virtualMachines@2021-03-01' = { + name: vmName_var + location: location + plan: vmPlan + identity: enableAAD || enableManagedIdentity ? { + type: 'systemAssigned' + } : null + properties: { + hardwareProfile: { + vmSize: vmSize + } + storageProfile: { + imageReference: vmImage + osDisk: { + name: '${vmName_var}-osdisk' + createOption: 'FromImage' + caching: 'ReadWrite' + diskSizeGB: 255 + managedDisk: { + storageAccountType: storageType_var + } + } + dataDisks: [for i in range(0, numDataDisks): { + lun: i + createOption: 'Empty' + diskSizeGB: dataDiskSize + }] + } + osProfile: { + computerName: vmName_var + adminUsername: adminName + adminPassword: adminPass + } + userData: base64(userData) + networkProfile: { + networkInterfaces: [ + { + id: nic.id + } + ] + } + } + tags: (contains(outTagsByResource, 'Microsoft.Compute/virtualMachines') ? union(tags_var, outTagsByResource['Microsoft.Compute/virtualMachines']) : tags_var) +} + +module remoteAccess 'remoteAccessExtension.bicep' = { + name: 'runRemoteAccess' + params: { + cmdGDKInstall: cmdGDKInstall + cmdTeradiciRegistration: cmdTeradiciRegistration + _artifactsLocation: _artifactsLocation + _artifactsLocationSasToken: _artifactsLocationSasToken + virtualMachineName: virtualMachine.name + remoteAccessTechnology: remoteAccessTechnology + location: location + fileShareStorageAccount: fileShareStorageAccount + fileShareStorageAccountKey: fileShareStorageAccountKey + fileShareName: fileShareName + p4Port: p4Port + p4Username: p4Username + p4Password: p4Password + p4Workspace: p4Workspace + p4Stream: p4Stream + p4ClientViews: p4ClientViews + } +} + +resource virtualMachine_enableAAD 'Microsoft.Compute/virtualMachines/extensions@2019-12-01' = if(enableAAD) { + name : '${virtualMachine.name}/AADLoginForWindows' + location : location + dependsOn : [ + remoteAccess + ] + properties: { + publisher: 'Microsoft.Azure.ActiveDirectory' + type: 'AADLoginForWindows' + typeHandlerVersion: '1.0' + autoUpgradeMinorVersion: true + } +} + +output Host_Name string = (!empty(publicIpId) ? reference(publicIpId, '2021-03-01').dnsSettings.fqdn : '') +output UserName string = adminName +output IPAddress string = (!empty(publicIpId) ? publicIpId : '') diff --git a/application-workloads/azure-gamedev/gamedev-vm/ibSetup.ps1 b/application-workloads/azure-gamedev/gamedev-vm/ibSetup.ps1 new file mode 100644 index 000000000000..edd9c7c09aac --- /dev/null +++ b/application-workloads/azure-gamedev/gamedev-vm/ibSetup.ps1 @@ -0,0 +1,77 @@ +param ( + [bool]$installIB=$true, + [bool]$registerIB=$false) + +$logFile = 'C:\Users\Public\Desktop\INSTALLED_SOFTWARE.txt' + +$vsPath = ${env:ProgramFiles(x86)} + '\Microsoft Visual Studio\Installer\vs_installer.exe' +$vsInstallPath = ${env:ProgramFiles(x86)} + '\Microsoft Visual Studio\2019\Community' + +$ibLicenseKey = '' + +try { + $userData = Invoke-RestMethod -Headers @{"Metadata"="true"} -Method GET -Uri "http://169.254.169.254/metadata/instance/compute/userData?api-version=2021-01-01&format=text" -TimeoutSec 5 + $userDataParameters = [System.Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($userData)).split(':') + foreach ($userDataParameter in $userDataParameters) { + if ($userDataParameter.StartsWith('ibLicenseKey=')) { + $ibLicenseKey = $userDataParameter.Replace('ibLicenseKey=', '') + break + } + } +} +catch {} + +if ($ibLicenseKey) { + + if ($installIB) { + try { + $arguments = ' modify --installpath "' + $vsInstallPath +'" --add Component.Incredibuild --quiet' + + $process = Start-Process -FilePath $vsPath -ArgumentList $arguments -WindowStyle Hidden -PassThru + $process | Wait-Process -Timeout 600 -ErrorAction SilentlyContinue -ErrorVariable stillRunning + if ($stillRunning) { + throw "Failed to configure Incredibuild" + } + + (Get-Content 'C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Startup\gdkinstall.cmd').replace('SET GDKSuccess=1', "powershell $PSCommandPath -installIB `$false -registerIB `$true`r`nSET GDKSuccess=1") | Set-Content 'C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Startup\gdkinstall.cmd' + } + catch [Exception] { + Add-Content $logFile 'ERROR: Incredibuild installation has failed. Please check the Incredibuild setup.' + } + } + + if ($registerIB) { + try { + Add-Type -AssemblyName PresentationCore,PresentationFramework + + $filename = $env:TEMP + '\ibLicenseKey.IB_lic' + $bytes = [Convert]::FromBase64String($ibLicenseKey) + [IO.File]::WriteAllBytes($filename, $bytes) + + $licenseMessage = & cmd /c $filename + + Remove-Item -Path $filename + + if ($licenseMessage.ToLower().Contains('success')) { + Add-Content $logFile '- Incredibuild' + [System.Windows.MessageBox]::Show("$licenseMessage If you delete your viritual machine, please remember to unload your Incredibuild license key first.", 'Incredibuild License Key', 'OK', 'Information') + } + else { + $msgBoxInput = [System.Windows.MessageBox]::Show("$licenseMessage Without a license key, all Unreal/Incredibuild builds will be limited to using 1 core. You can load a license key manually.`r`n`r`nAlternately, do you want to uninstall Incredibuild now?", 'Uninstall Incredibuild?', 'YesNo', 'Warning') + switch ($msgBoxInput) { + 'Yes' { + $arguments = ' modify --installpath "' + $vsInstallPath +'" --remove Component.Incredibuild --quiet' + Start-Process -FilePath $vsPath -ArgumentList $arguments -WindowStyle Hidden | Out-Null + } + 'No' { + Add-Content $logFile '- Incredibuild' + Add-Content $logFile "ERROR: Your Incredibuild license failed to load with message: $licenseMessage" + } + } + } + } + catch [Exception]{ + Add-Content $logFile "ERROR: Loading of the Incredibuild license key has failed. Please check the Incredibuild settings. Details: $PSItem" + } + } +} \ No newline at end of file diff --git a/application-workloads/azure-gamedev/gamedev-vm/main.bicep b/application-workloads/azure-gamedev/gamedev-vm/main.bicep index 75404fffa726..d7f0d656349c 100644 --- a/application-workloads/azure-gamedev/gamedev-vm/main.bicep +++ b/application-workloads/azure-gamedev/gamedev-vm/main.bicep @@ -1,105 +1,79 @@ -/* Copyright (c) Microsoft Corporation. All rights reserved. - This Template deploys a Industrial AI Application -*/ -/* Application Selection - Each application must provide a Plan and a Parameter Mapping. - */ - param apps object = { - gamingDevVM : gamingDevVM - } - /* Application Plans - Each Managed Application requires a plan object. This may be overriden as a parameter input. - */ - param gamingVMPlan object = { - name : 'gaming-dev-vm-ama' - product : 'f39e77ad-ada5-4145-9291-643e6e3ce1b2' - publisher: publisher - version : '0.1.4' - } - /* Application Parameter Mapping - Each Managed Application requires a parameter mapping. - */ - param gamingDevVM object = { - plan: gamingVMPlan - parameters: { - location: { - value: location - } - vmSize: { - value: 'Standard_NV12s_v3' - } - adminName: { - value: administratorLogin - } - adminPass: { - value: passwordAdministratorLogin - } - osType: { - value: osType - } - gameEngine: { - value: gameEngine - } - } - } -// - -// Parameters - param jitAccessEnabled bool = false - param location string = resourceGroup().location - param publisher string = 'microsoftcorporation1602274591143' // Prod Account - param applicationResourceName string = '611f626e14154c0fb7f29099c2ff95a0' - param managedResourceGroupId string = '' - - // Datastore Parmeters - param storageAccountNewOrExisting string = 'existing' - param storageAccountResourceGroup string = '' - param storageAccount string = '' - param dataContainer string = 'retailidmsampledata' - param cosmosDB string = '' - param dataExplorer string = '' +param location string = resourceGroup().location - // GamingVM Parameters - param gameEngine string = 'ue_4_27' - param osType string = 'win10' +@allowed([ + 'ue_4_27' + 'ue_5_0ea' +]) +param gameEngine string = 'ue_4_27' +@allowed([ + 'win10' + 'ws2019' +]) +param osType string = 'win10' - // Endpoint Parameters - param aksAgentCount int = 3 +@allowed([ + 'Standard_NC4as_T4_v3' + 'Standard_NC8as_T4_v3' + 'Standard_NC16as_T4_v3' + 'Standard_NC64as_T4_v3' + 'Standard_NV6' + 'Standard_NV12' + 'Standard_NV24' + 'Standard_NV12s_v3' + 'Standard_NV24s_v3' + 'Standard_NV48s_v3' +]) +param vmSize string = 'Standard_NV12s_v3' - // Discrete Parameters - @allowed([ - 'gamingDevVM' - ]) - param solution string +param administratorLogin string +@secure() +param passwordAdministratorLogin string - @allowed([ - 'true' - 'false' - ]) - param crossTenant string = 'true' +@description('Remote Access technology') +@allowed([ + 'RDP' + 'Teradici' + 'Parsec' +]) +param remoteAccessTechnology string = 'RDP' +param managedResourceGroupId string = '' - // Secure Parameters - @secure() - param servicePrincipalClientID string = '' - - @secure() - param servicePrincipalSecret string = '' -// - -// Resources - resource solution_resource 'Microsoft.Solutions/applications@2017-09-01' = { - name : solution - location: location - kind : 'MarketPlace' - plan : apps[solution].plan - properties: { - managedResourceGroupId: (empty(managedResourceGroupId) ? '${subscription().id}/resourceGroups/${take('${resourceGroup().name}-mrg', 90)}' : managedResourceGroupId) - parameters : apps[solution].parameters - jitAccessPolicy : { - jitAccessEnabled : jitAccessEnabled - } +resource solution_resource 'Microsoft.Solutions/applications@2017-09-01' = { + name : 'gamingDevVM' + location: location + kind : 'MarketPlace' + plan : { + 'name': 'game-dev-vm-amatest-plan' + 'product': 'f39e77ad-ada5-4145-9291-643e6e3ce1b2' + 'publisher': 'microsoftcorporation1602274591143' + 'version': '0.1.121' + } + properties: { + managedResourceGroupId: (empty(managedResourceGroupId) ? '${subscription().id}/resourceGroups/${take('${resourceGroup().name}-mrg', 90)}' : managedResourceGroupId) + parameters: { + location: { + value: location + } + vmSize: { + value: vmSize + } + adminName: { + value: administratorLogin + } + adminPass: { + value: passwordAdministratorLogin + } + osType: { + value: osType + } + gameEngine: { + value: gameEngine + } + remoteAccessTechnology: { + value: remoteAccessTechnology + } } } -// +} diff --git a/application-workloads/azure-gamedev/gamedev-vm/main_st.bicep b/application-workloads/azure-gamedev/gamedev-vm/main_st.bicep new file mode 100644 index 000000000000..4f7fe0f1c25d --- /dev/null +++ b/application-workloads/azure-gamedev/gamedev-vm/main_st.bicep @@ -0,0 +1,52 @@ + +param location string = resourceGroup().location + +@allowed([ + 'ue_4_27' + 'ue_5_0ea' +]) +param gameEngine string = 'ue_4_27' +@allowed([ + 'win10' + 'ws2019' +]) +param osType string = 'win10' + +@allowed([ + 'Standard_NC4as_T4_v3' + 'Standard_NC8as_T4_v3' + 'Standard_NC16as_T4_v3' + 'Standard_NC64as_T4_v3' + 'Standard_NV6' + 'Standard_NV12' + 'Standard_NV24' + 'Standard_NV12s_v3' + 'Standard_NV24s_v3' + 'Standard_NV48s_v3' +]) +param vmSize string = 'Standard_NV12s_v3' + +param administratorLogin string +@secure() +param passwordAdministratorLogin string + +@description('Remote Access technology') +@allowed([ + 'RDP' + 'Teradici' + 'Parsec' +]) +param remoteAccessTechnology string = 'RDP' + +module gamedevvm 'gamedev-vm.bicep' = { + name : 'gamingDevVM' + params: { + location: location + vmSize: vmSize + adminName: administratorLogin + adminPass: passwordAdministratorLogin + osType: osType + gameEngine: gameEngine + remoteAccessTechnology: remoteAccessTechnology + } +} diff --git a/application-workloads/azure-gamedev/gamedev-vm/nsgRules.bicep b/application-workloads/azure-gamedev/gamedev-vm/nsgRules.bicep new file mode 100644 index 000000000000..8560bbc14aa8 --- /dev/null +++ b/application-workloads/azure-gamedev/gamedev-vm/nsgRules.bicep @@ -0,0 +1,239 @@ +param addPixelStreamingPorts bool = false + +var nsgRules = { + 'nsgRules-RDP': !addPixelStreamingPorts ? [ + { + name: 'RDP' + properties: { + priority: 1010 + protocol: '*' + access: 'Allow' + direction: 'Inbound' + sourceAddressPrefix: '*' + sourcePortRange: '*' + destinationAddressPrefix: '*' + destinationPortRange: '3389' + } + } + ] : [ + { + name: 'RDP' + properties: { + priority: 1010 + protocol: '*' + access: 'Allow' + direction: 'Inbound' + sourceAddressPrefix: '*' + sourcePortRange: '*' + destinationAddressPrefix: '*' + destinationPortRange: '3389' + } + } + { + name: 'PixelStream' + properties: { + priority: 1020 + protocol: '*' + access: 'Allow' + direction: 'Inbound' + sourceAddressPrefix: '*' + sourcePortRange: '*' + destinationAddressPrefix: '*' + destinationPortRange: '80' + } + } + ] + + + 'nsgRules-Teradici': !addPixelStreamingPorts ? [ + { + name: 'RDP' + properties: { + priority: 1010 + protocol: '*' + access: 'Allow' + direction: 'Inbound' + sourceAddressPrefix: '*' + sourcePortRange: '*' + destinationAddressPrefix: '*' + destinationPortRange: '3389' + } + } + { + name: 'PCoIPtcp' + properties: { + priority: 1020 + protocol: 'TCP' + access: 'Allow' + direction: 'Inbound' + sourceAddressPrefix: '*' + sourcePortRange: '*' + destinationAddressPrefix: '*' + destinationPortRange: '4172' + } + } + { + name: 'PCoIPudp' + properties: { + priority: 1030 + protocol: 'UDP' + access: 'Allow' + direction: 'Inbound' + sourceAddressPrefix: '*' + sourcePortRange: '*' + destinationAddressPrefix: '*' + destinationPortRange: '4172' + } + } + { + name: 'CertAuthHTTPS' + properties: { + priority: 1040 + protocol: 'TCP' + access: 'Allow' + direction: 'Inbound' + sourceAddressPrefix: '*' + sourcePortRange: '*' + destinationAddressPrefix: '*' + destinationPortRange: '443' + } + } + { + name: 'TeradiciCom' + properties: { + priority: 1050 + protocol: 'TCP' + access: 'Allow' + direction: 'Inbound' + sourceAddressPrefix: '*' + sourcePortRange: '*' + destinationAddressPrefix: '*' + destinationPortRange: '60443' + } + } + ] : [ + { + name: 'RDP' + properties: { + priority: 1010 + protocol: '*' + access: 'Allow' + direction: 'Inbound' + sourceAddressPrefix: '*' + sourcePortRange: '*' + destinationAddressPrefix: '*' + destinationPortRange: '3389' + } + } + { + name: 'PCoIPtcp' + properties: { + priority: 1020 + protocol: 'TCP' + access: 'Allow' + direction: 'Inbound' + sourceAddressPrefix: '*' + sourcePortRange: '*' + destinationAddressPrefix: '*' + destinationPortRange: '4172' + } + } + { + name: 'PCoIPudp' + properties: { + priority: 1030 + protocol: 'UDP' + access: 'Allow' + direction: 'Inbound' + sourceAddressPrefix: '*' + sourcePortRange: '*' + destinationAddressPrefix: '*' + destinationPortRange: '4172' + } + } + { + name: 'CertAuthHTTPS' + properties: { + priority: 1040 + protocol: 'TCP' + access: 'Allow' + direction: 'Inbound' + sourceAddressPrefix: '*' + sourcePortRange: '*' + destinationAddressPrefix: '*' + destinationPortRange: '443' + } + } + { + name: 'TeradiciCom' + properties: { + priority: 1050 + protocol: 'TCP' + access: 'Allow' + direction: 'Inbound' + sourceAddressPrefix: '*' + sourcePortRange: '*' + destinationAddressPrefix: '*' + destinationPortRange: '60443' + } + } + { + name: 'PixelStream' + properties: { + priority: 1060 + protocol: '*' + access: 'Allow' + direction: 'Inbound' + sourceAddressPrefix: '*' + sourcePortRange: '*' + destinationAddressPrefix: '*' + destinationPortRange: '80' + } + } + ] + + 'nsgRules-Parsec': !addPixelStreamingPorts ? [ + { + name: 'RDP' + properties: { + priority: 1010 + protocol: '*' + access: 'Allow' + direction: 'Inbound' + sourceAddressPrefix: '*' + sourcePortRange: '*' + destinationAddressPrefix: '*' + destinationPortRange: '3389' + } + } + ] : [ + { + name: 'RDP' + properties: { + priority: 1010 + protocol: '*' + access: 'Allow' + direction: 'Inbound' + sourceAddressPrefix: '*' + sourcePortRange: '*' + destinationAddressPrefix: '*' + destinationPortRange: '3389' + } + } + { + name: 'PixelStream' + properties: { + priority: 1020 + protocol: '*' + access: 'Allow' + direction: 'Inbound' + sourceAddressPrefix: '*' + sourcePortRange: '*' + destinationAddressPrefix: '*' + destinationPortRange: '80' + } + } + ] +} + +output nsgRules object = nsgRules diff --git a/application-workloads/azure-gamedev/gamedev-vm/p4DepotSync.ps1 b/application-workloads/azure-gamedev/gamedev-vm/p4DepotSync.ps1 new file mode 100644 index 000000000000..ee0f456f56da --- /dev/null +++ b/application-workloads/azure-gamedev/gamedev-vm/p4DepotSync.ps1 @@ -0,0 +1,84 @@ +param ( + [string]$p4Path="C:\p4depots", + [string]$p4Port="", + [string]$p4Username="", + [string]$p4Password="", + [string]$p4Workspace="", + [string]$p4Stream="", + [string]$p4ClientViews="") + + +if ($p4Port -and $p4Username -and $p4Password) +{ + + try { + $driveLetter = (Get-StoragePool | Get-Volume).DriveLetter + if ($driveLetter) + { + $p4Path = $driveLetter + ':' + (Split-Path -Path $p4Path -NoQualifier) + } + + $viewsAdded=$false + $clientViews = [System.Text.Encoding]::ASCII.GetString([System.Convert]::FromBase64String($p4ClientViews)) + + $configFile = "p4config.config" + New-Item -ItemType 'directory' -Path $p4Path + Add-Content -Path "$p4Path\$configFile" -Value "P4PORT=$p4port" + Add-Content -Path "$p4Path\$configFile" -Value "P4USER=$p4Username" + Add-Content -Path "$p4Path\$configFile" -Value "P4CLIENT=$p4Workspace" + + & "C:\Program Files\Perforce\p4.exe" set -s P4CONFIG="$p4Path\$configFile" + + if ($p4Port.ToLower().StartsWith("ssl:")) + { + & "C:\Program Files\Perforce\p4.exe" trust -y + } + + echo $p4Password | p4 login + + $p4ClientSpec = & "C:\Program Files\Perforce\p4.exe" client -o + $p4ClientSpec = $p4ClientSpec -replace '^Root:.+$', "Root:`t$p4Path" + $p4ClientSpec = $p4ClientSpec -replace '^Host:.+$', "Host:`t$env:Computername" + + $p4ClientSpecUpdate = @() + for($i = 0; $i -lt $p4ClientSpec.Length; $i++) + { + $p4ClientSpecUpdate += $p4ClientSpec[$i] + + if($p4ClientSpec[$i] -eq "View:") + { + $viewsJson = ConvertFrom-Json -InputObject $clientViews.Replace("\", "") + for($j = 0; $j -lt $viewsJson.length; $j++) + { + $p4ClientSpecUpdate += "`t" + $viewsJson[$j].depotPath + "`t//" + $p4Workspace + $viewsJson[$j].clientPath + } + $viewsAdded=$true + break + } + + } + + if (-Not $viewsAdded) + { + $p4ClientSpecUpdate += "View:" + + $viewsJson = ConvertFrom-Json -InputObject $clientViews.Replace("\", "") + for($j = 0; $j -lt $viewsJson.length; $j++) + { + $p4ClientSpecUpdate += "`t" + $viewsJson[$j].depotPath + "`t//" + $p4Workspace + $viewsJson[$j].clientPath + } + } + + if ($p4Stream) + { + $p4ClientSpecUpdate = $p4ClientSpecUpdate -replace '^Stream:.+$', "" + $p4ClientSpecUpdate += "Stream:`t$p4Stream" + } + + $p4ClientSpecUpdate | & "C:\Program Files\Perforce\p4.exe" client -i + Start-Process -FilePath "C:\Program Files\Perforce\p4.exe" -ArgumentList " sync -f" -WindowStyle Hidden + } + catch [Exception]{ + Add-Content 'C:\Users\Public\Desktop\INSTALLED_SOFTWARE.txt' 'ERROR: Perforce depot sync has failed. Please use Perforce client tools to manually sync your depot.' + } +} \ No newline at end of file diff --git a/application-workloads/azure-gamedev/gamedev-vm/remoteAccessExtension.bicep b/application-workloads/azure-gamedev/gamedev-vm/remoteAccessExtension.bicep new file mode 100644 index 000000000000..75ab80ddabf4 --- /dev/null +++ b/application-workloads/azure-gamedev/gamedev-vm/remoteAccessExtension.bicep @@ -0,0 +1,80 @@ +param cmdGDKInstall string +param cmdTeradiciRegistration string +param _artifactsLocation string +param _artifactsLocationSasToken string +param virtualMachineName string +param remoteAccessTechnology string +param location string +param fileShareStorageAccount string +param fileShareStorageAccountKey string +param fileShareName string +param p4Port string +param p4Username string +param p4Password string +param p4Workspace string +param p4Stream string +param p4ClientViews string + +var mountFileShareParams = '-storageAccount \'${fileShareStorageAccount}\' -storageAccountKey \'${fileShareStorageAccountKey}\' -fileShareName \'${fileShareName}\'' +var p4Params = '-p4Port \'${p4Port}\' -p4Username \'${p4Username}\' -p4Password \'${p4Password}\' -p4Workspace \'${p4Workspace}\' -p4Stream \'${p4Stream}\' -p4ClientViews \'${p4ClientViews}\'' + +var remoteAccessExtension = { + RDP: { + publisher : 'Microsoft.Compute' + type : 'CustomScriptExtension' + typeHandlerVersion : '1.10' + autoUpgradeMinorVersion: true + settings: { + fileUris: [ + uri(_artifactsLocation, 'CreateDataDisk.ps1${_artifactsLocationSasToken}') + uri(_artifactsLocation, 'MountFileShare.ps1${_artifactsLocationSasToken}') + uri(_artifactsLocation, 'p4DepotSync.ps1${_artifactsLocationSasToken}') + uri(_artifactsLocation, 'ibSetup.ps1${_artifactsLocationSasToken}') + ] + commandToExecute: 'powershell -ExecutionPolicy Unrestricted -NoProfile -NonInteractive -command "./CreateDataDisk.ps1;./MountFileShare.ps1 ${mountFileShareParams};./p4DepotSync.ps1 ${p4Params};./ibSetup.ps1;${cmdGDKInstall}"' + } + } + Teradici: { + publisher : 'Microsoft.Compute' + type : 'CustomScriptExtension' + typeHandlerVersion : '1.10' + autoUpgradeMinorVersion: true + settings: { + fileUris: [ + uri(_artifactsLocation, 'CreateDataDisk.ps1${_artifactsLocationSasToken}') + uri(_artifactsLocation, 'MountFileShare.ps1${_artifactsLocationSasToken}') + uri(_artifactsLocation, 'p4DepotSync.ps1${_artifactsLocationSasToken}') + uri(_artifactsLocation, 'ibSetup.ps1${_artifactsLocationSasToken}') + uri(_artifactsLocation, 'TeradiciRegCAS.ps1${_artifactsLocationSasToken}') + ] + } + protectedSettings: { + commandToExecute: 'powershell -ExecutionPolicy Unrestricted -command "./CreateDataDisk.ps1;./MountFileShare.ps1 ${mountFileShareParams};./p4DepotSync.ps1 ${p4Params};./ibSetup.ps1;${cmdGDKInstall};${cmdTeradiciRegistration}"' + } + } + Parsec: { + publisher : 'Microsoft.Compute' + type : 'CustomScriptExtension' + typeHandlerVersion : '1.10' + autoUpgradeMinorVersion: true + settings: { + fileUris: [ + uri(_artifactsLocation, 'CreateDataDisk.ps1${_artifactsLocationSasToken}') + uri(_artifactsLocation, 'MountFileShare.ps1${_artifactsLocationSasToken}') + uri(_artifactsLocation, 'p4DepotSync.ps1${_artifactsLocationSasToken}') + uri(_artifactsLocation, 'ibSetup.ps1${_artifactsLocationSasToken}') + uri(_artifactsLocation, 'PostInstall.ps1${_artifactsLocationSasToken}') + uri(_artifactsLocation, 'PreInstall.zip${_artifactsLocationSasToken}') + ] + commandToExecute: 'powershell -ExecutionPolicy Unrestricted -NoProfile -NonInteractive -command "./CreateDataDisk.ps1;./MountFileShare.ps1 ${mountFileShareParams};./p4DepotSync.ps1 ${p4Params};./ibSetup.ps1;${cmdGDKInstall};./PostInstall.ps1"' + } + } +} + +resource virtualMachine_remoteAccessExtension 'Microsoft.Compute/virtualMachines/extensions@2019-12-01' = { + name : '${virtualMachineName}/CustomScriptExtension-${remoteAccessTechnology}' + location : location + properties: remoteAccessExtension[remoteAccessTechnology] +} + +output id string = virtualMachine_remoteAccessExtension.id From ae842539a72785ff11910b496e33a2301e353e46 Mon Sep 17 00:00:00 2001 From: Daniel Ciborowski Date: Mon, 4 Apr 2022 16:11:28 -0400 Subject: [PATCH 11/71] adding Solution Template --- .../azure-gamedev/gamedev-vm/main_st.bicep | 52 ------------------- 1 file changed, 52 deletions(-) delete mode 100644 application-workloads/azure-gamedev/gamedev-vm/main_st.bicep diff --git a/application-workloads/azure-gamedev/gamedev-vm/main_st.bicep b/application-workloads/azure-gamedev/gamedev-vm/main_st.bicep deleted file mode 100644 index 4f7fe0f1c25d..000000000000 --- a/application-workloads/azure-gamedev/gamedev-vm/main_st.bicep +++ /dev/null @@ -1,52 +0,0 @@ - -param location string = resourceGroup().location - -@allowed([ - 'ue_4_27' - 'ue_5_0ea' -]) -param gameEngine string = 'ue_4_27' -@allowed([ - 'win10' - 'ws2019' -]) -param osType string = 'win10' - -@allowed([ - 'Standard_NC4as_T4_v3' - 'Standard_NC8as_T4_v3' - 'Standard_NC16as_T4_v3' - 'Standard_NC64as_T4_v3' - 'Standard_NV6' - 'Standard_NV12' - 'Standard_NV24' - 'Standard_NV12s_v3' - 'Standard_NV24s_v3' - 'Standard_NV48s_v3' -]) -param vmSize string = 'Standard_NV12s_v3' - -param administratorLogin string -@secure() -param passwordAdministratorLogin string - -@description('Remote Access technology') -@allowed([ - 'RDP' - 'Teradici' - 'Parsec' -]) -param remoteAccessTechnology string = 'RDP' - -module gamedevvm 'gamedev-vm.bicep' = { - name : 'gamingDevVM' - params: { - location: location - vmSize: vmSize - adminName: administratorLogin - adminPass: passwordAdministratorLogin - osType: osType - gameEngine: gameEngine - remoteAccessTechnology: remoteAccessTechnology - } -} From febeb7cd38ca43a58f30d5dd32cbe690c378b879 Mon Sep 17 00:00:00 2001 From: Daniel Ciborowski Date: Mon, 4 Apr 2022 16:19:30 -0400 Subject: [PATCH 12/71] cleanup synapse code --- modules/ml-synapse/README.md | 16 -- modules/ml-synapse/azuredeploy.json | 231 ------------------ .../ml-synapse/azuredeploy.parameters.json | 15 -- modules/ml-synapse/main.bicep | 167 ------------- modules/ml-synapse/metadata.json | 9 - 5 files changed, 438 deletions(-) delete mode 100644 modules/ml-synapse/README.md delete mode 100644 modules/ml-synapse/azuredeploy.json delete mode 100644 modules/ml-synapse/azuredeploy.parameters.json delete mode 100644 modules/ml-synapse/main.bicep delete mode 100644 modules/ml-synapse/metadata.json diff --git a/modules/ml-synapse/README.md b/modules/ml-synapse/README.md deleted file mode 100644 index 67b79540e509..000000000000 --- a/modules/ml-synapse/README.md +++ /dev/null @@ -1,16 +0,0 @@ -# Blank Template - -![Azure Public Test Date](https://azurequickstartsservice.blob.core.windows.net/badges/module/ml-synpase/PublicLastTestDate.svg) -![Azure Public Test Result](https://azurequickstartsservice.blob.core.windows.net/badges/module/ml-synpase/PublicDeployment.svg) - -![Azure US Gov Last Test Date](https://azurequickstartsservice.blob.core.windows.net/badges/module/ml-synpase/FairfaxLastTestDate.svg) -![Azure US Gov Last Test Result](https://azurequickstartsservice.blob.core.windows.net/badges/module/ml-synpase/FairfaxDeployment.svg) - -![Best Practice Check](https://azurequickstartsservice.blob.core.windows.net/badges/module/ml-synpase/BestPracticeResult.svg) -![Cred Scan Check](https://azurequickstartsservice.blob.core.windows.net/badges/module/ml-synpase/CredScanResult.svg) - -[![Deploy To Azure](https://raw.githubusercontent.com/Azure/azure-quickstart-templates/master/1-CONTRIBUTION-GUIDE/images/deploytoazure.svg?sanitize=true)](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2FAzure%2Fazure-quickstart-templates%2Fmaster%2Fmodule%2Fml-synpase%2Fazuredeploy.json) -[![Deploy To Azure US Gov](https://raw.githubusercontent.com/Azure/azure-quickstart-templates/master/1-CONTRIBUTION-GUIDE/images/deploytoazuregov.svg?sanitize=true)](https://portal.azure.us/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2FAzure%2Fazure-quickstart-templates%2Fmaster%2Fmodule%2Fml-synpase%2Fazuredeploy.json) -[![Visualize](https://raw.githubusercontent.com/Azure/azure-quickstart-templates/master/1-CONTRIBUTION-GUIDE/images/visualizebutton.svg?sanitize=true)](http://armviz.io/#/?load=https%3A%2F%2Fraw.githubusercontent.com%2FAzure%2Fazure-quickstart-templates%2Fmaster%2Fmodule%2Fml-synpase%2Fazuredeploy.json) - -`Tags: empty, blank` diff --git a/modules/ml-synapse/azuredeploy.json b/modules/ml-synapse/azuredeploy.json deleted file mode 100644 index ae6de9a4f2a9..000000000000 --- a/modules/ml-synapse/azuredeploy.json +++ /dev/null @@ -1,231 +0,0 @@ -{ - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.4.1318.3566", - "templateHash": "16992569491940042489" - } - }, - "parameters": { - "name": { - "type": "string", - "defaultValue": "[newGuid()]" - }, - "location": { - "type": "string" - }, - "tags": { - "type": "object", - "defaultValue": { - "aia-industry": "industry", - "aia-solution ": "solution", - "version": "0.0.0" - } - }, - "defaultDataLakeStorageFilesystemName": { - "type": "string", - "defaultValue": "workspace" - }, - "storageLocation": { - "type": "string", - "defaultValue": "[parameters('location')]" - }, - "cmkUri": { - "type": "string", - "defaultValue": "" - }, - "storageAccountName": { - "type": "string", - "defaultValue": "[uniqueString(resourceGroup().id, parameters('name'))]" - }, - "azureMLName": { - "type": "string", - "defaultValue": "[format('aml{0}', uniqueString(resourceGroup().id, parameters('name')))]" - }, - "appInsights": { - "type": "string", - "defaultValue": "[format('appin{0}', uniqueString(resourceGroup().id, parameters('name')))]" - }, - "keyVault": { - "type": "string", - "defaultValue": "[format('kv{0}', uniqueString(resourceGroup().id, parameters('name')))]" - } - }, - "variables": { - "azureMLSku": "basic", - "kind": "StorageV2", - "storageBlobDataContributorRoleID": "ba92f5b4-2d11-453d-a403-e96b0029c9fe", - "defaultDataLakeStorageAccountName": "[uniqueString(resourceGroup().id, parameters('name'))]", - "defaultDataLakeStorageAccountUrl": "[format('https://{0}.dfs.{1}', variables('defaultDataLakeStorageAccountName'), environment().suffixes.storage)]", - "cmkUriStripVersion": "[if(empty(parameters('cmkUri')), '', substring(parameters('cmkUri'), 0, lastIndexOf(parameters('cmkUri'), '/')))]", - "withCmk": { - "cmk": { - "key": { - "name": "default", - "keyVaultUrl": "[variables('cmkUriStripVersion')]" - } - } - }, - "encryption": "[if(empty(parameters('cmkUri')), json('{}'), variables('withCmk'))]" - }, - "resources": [ - { - "type": "Microsoft.Storage/storageAccounts", - "apiVersion": "2019-04-01", - "name": "[parameters('storageAccountName')]", - "location": "[parameters('location')]", - "tags": { - "Type": "Synapse Data Lake Storage", - "Created with": "Synapse Azure Resource Manager deployment template" - }, - "sku": { - "name": "Standard_RAGRS" - }, - "kind": "[variables('kind')]", - "properties": { - "isHnsEnabled": true, - "networkAcls": { - "bypass": "AzureServices", - "defaultAction": "Allow" - }, - "supportsHttpsTrafficOnly": true, - "encryption": { - "services": { - "file": { - "enabled": true - }, - "blob": { - "enabled": true - } - }, - "keySource": "Microsoft.Storage" - }, - "accessTier": "Hot" - } - }, - { - "type": "Microsoft.Storage/storageAccounts/blobServices", - "apiVersion": "2019-04-01", - "name": "[format('{0}/{1}', parameters('storageAccountName'), 'default')]", - "properties": { - "deleteRetentionPolicy": { - "enabled": false - } - }, - "dependsOn": [ - "[resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName'))]" - ] - }, - { - "type": "Microsoft.Storage/storageAccounts/blobServices/containers", - "apiVersion": "2019-04-01", - "name": "[format('{0}/{1}/{2}', parameters('storageAccountName'), 'default', 'workspace')]", - "properties": { - "publicAccess": "None" - }, - "dependsOn": [ - "[resourceId('Microsoft.Storage/storageAccounts/blobServices', parameters('storageAccountName'), 'default')]", - "[resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName'))]" - ] - }, - { - "type": "Microsoft.Synapse/workspaces", - "apiVersion": "2019-06-01-preview", - "name": "[parameters('name')]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]", - "identity": { - "type": "SystemAssigned" - }, - "properties": { - "defaultDataLakeStorage": { - "accountUrl": "[variables('defaultDataLakeStorageAccountUrl')]", - "filesystem": "[parameters('defaultDataLakeStorageFilesystemName')]" - }, - "encryption": "[variables('encryption')]" - }, - "dependsOn": [ - "[resourceId('Microsoft.Storage/storageAccounts/blobServices', parameters('storageAccountName'), 'default')]", - "[resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName'))]" - ] - }, - { - "type": "Microsoft.Synapse/workspaces/firewallRules", - "apiVersion": "2019-06-01-preview", - "name": "[format('{0}/{1}', parameters('name'), 'allowAll')]", - "properties": { - "startIpAddress": "0.0.0.0", - "endIpAddress": "255.255.255.255" - }, - "dependsOn": [ - "[resourceId('Microsoft.Synapse/workspaces', parameters('name'))]" - ] - }, - { - "type": "Microsoft.Storage/storageAccounts/blobServices/containers/providers/roleAssignments", - "apiVersion": "2018-09-01-preview", - "name": "[format('{0}/default/{1}/Microsoft.Authorization/{2}', variables('defaultDataLakeStorageAccountName'), parameters('defaultDataLakeStorageFilesystemName'), guid(format('{0}/{1}/storageRoleDeploymentResource', resourceGroup().id, variables('storageBlobDataContributorRoleID'))))]", - "location": "[parameters('storageLocation')]", - "properties": { - "roleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', variables('storageBlobDataContributorRoleID'))]", - "principalId": "[reference(format('Microsoft.Synapse/workspaces/{0}', parameters('name')), '2019-06-01-preview', 'Full').identity.principalId]", - "principalType": "ServicePrincipal" - } - }, - { - "type": "Microsoft.KeyVault/vaults", - "apiVersion": "2019-09-01", - "name": "[parameters('keyVault')]", - "tags": "[parameters('tags')]", - "location": "[parameters('location')]", - "properties": { - "accessPolicies": [], - "enableSoftDelete": true, - "softDeleteRetentionInDays": 7, - "sku": { - "family": "A", - "name": "standard" - }, - "tenantId": "[subscription().tenantId]" - } - }, - { - "type": "Microsoft.Insights/components", - "apiVersion": "2020-02-02-preview", - "name": "[parameters('appInsights')]", - "tags": "[parameters('tags')]", - "location": "[if(or(equals(parameters('location'), 'eastus2'), equals(parameters('location'), 'westcentralus')), 'southcentralus', parameters('location'))]", - "kind": "web", - "properties": { - "Application_Type": "web" - } - }, - { - "type": "Microsoft.MachineLearningServices/workspaces", - "apiVersion": "2020-06-01", - "name": "[parameters('azureMLName')]", - "tags": "[parameters('tags')]", - "location": "[parameters('location')]", - "sku": { - "name": "[variables('azureMLSku')]", - "tier": "[variables('azureMLSku')]" - }, - "properties": { - "applicationInsights": "[resourceId('Microsoft.Insights/components', parameters('appInsights'))]", - "friendlyName": "[parameters('azureMLName')]", - "keyVault": "[resourceId('Microsoft.KeyVault/vaults', parameters('keyVault'))]", - "storageAccount": "[resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName'))]" - }, - "identity": { - "type": "SystemAssigned" - }, - "dependsOn": [ - "[resourceId('Microsoft.Insights/components', parameters('appInsights'))]", - "[resourceId('Microsoft.KeyVault/vaults', parameters('keyVault'))]", - "[resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName'))]" - ] - } - ] -} diff --git a/modules/ml-synapse/azuredeploy.parameters.json b/modules/ml-synapse/azuredeploy.parameters.json deleted file mode 100644 index 489a20a5769f..000000000000 --- a/modules/ml-synapse/azuredeploy.parameters.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", - "contentVersion": "1.0.0.0", - "parameters": { - "_artifactsLocation": { - "value": "" - }, - "_artifactsLocationSasToken": { - "value": "" - }, - "location": { - "value": "eastus" - } - } -} diff --git a/modules/ml-synapse/main.bicep b/modules/ml-synapse/main.bicep deleted file mode 100644 index 3f49a604e255..000000000000 --- a/modules/ml-synapse/main.bicep +++ /dev/null @@ -1,167 +0,0 @@ -param name string = newGuid() -param location string -param tags object = { - 'aia-industry': 'industry' - 'aia-solution ': 'solution' - version: '0.0.0' -} - -param defaultDataLakeStorageFilesystemName string = 'workspace' -param storageLocation string = location -param cmkUri string = '' -param storageAccountName string = uniqueString(resourceGroup().id, name) -param azureMLName string = 'aml${uniqueString(resourceGroup().id, name)}' -param appInsights string = 'appin${uniqueString(resourceGroup().id, name)}' -param keyVault string = 'kv${uniqueString(resourceGroup().id, name)}' - -var azureMLSku = 'basic' -var kind = 'StorageV2' -var storageBlobDataContributorRoleID = 'ba92f5b4-2d11-453d-a403-e96b0029c9fe' -var defaultDataLakeStorageAccountName = uniqueString(resourceGroup().id, name) -var defaultDataLakeStorageAccountUrl = 'https://${defaultDataLakeStorageAccountName}.dfs.${environment().suffixes.storage}' -var cmkUriStripVersion = (empty(cmkUri) ? '' : substring(cmkUri, 0, lastIndexOf(cmkUri, '/'))) -var withCmk = { - cmk: { - key: { - name: 'default' - keyVaultUrl: cmkUriStripVersion - } - } -} -var encryption = (empty(cmkUri) ? json('{}') : withCmk) - -resource storageAccount 'Microsoft.Storage/storageAccounts@2019-04-01' = { - name: storageAccountName - location: location - tags: { - Type: 'Synapse Data Lake Storage' - 'Created with': 'Synapse Azure Resource Manager deployment template' - } - sku: { - name: 'Standard_RAGRS' - } - kind: kind - properties: { - isHnsEnabled: true - networkAcls: { - bypass: 'AzureServices' - defaultAction: 'Allow' - } - supportsHttpsTrafficOnly: true - encryption: { - services: { - file: { - enabled: true - } - blob: { - enabled: true - } - } - keySource: 'Microsoft.Storage' - } - accessTier: 'Hot' - } -} - -resource blob 'Microsoft.Storage/storageAccounts/blobServices@2019-04-01' = { - parent: storageAccount - name: 'default' - properties: { - deleteRetentionPolicy: { - enabled: false - } - } -} - -resource container 'Microsoft.Storage/storageAccounts/blobServices/containers@2019-04-01' = { - parent: blob - name: 'workspace' - properties: { - publicAccess: 'None' - } -} - -resource synapseWorkspace 'Microsoft.Synapse/workspaces@2019-06-01-preview' = { - name: name - location: location - tags: tags - identity: { - type: 'SystemAssigned' - } - properties: { - defaultDataLakeStorage: { - accountUrl: defaultDataLakeStorageAccountUrl - filesystem: defaultDataLakeStorageFilesystemName - } - encryption: encryption - } - dependsOn: [ - blob - storageAccount - ] -} - -resource firewallRules 'Microsoft.Synapse/workspaces/firewallrules@2019-06-01-preview' = { - parent: synapseWorkspace - name: 'allowAll' - properties: { - startIpAddress: '0.0.0.0' - endIpAddress: '255.255.255.255' - } -} - -resource roleAssignments 'Microsoft.Storage/storageAccounts/blobServices/containers/providers/roleAssignments@2018-09-01-preview' = { - name: '${defaultDataLakeStorageAccountName}/default/${defaultDataLakeStorageFilesystemName}/Microsoft.Authorization/${guid('${resourceGroup().id}/${storageBlobDataContributorRoleID}/storageRoleDeploymentResource')}' - location: storageLocation - properties: { - roleDefinitionId: resourceId('Microsoft.Authorization/roleDefinitions', storageBlobDataContributorRoleID) - principalId: reference('Microsoft.Synapse/workspaces/${name}', '2019-06-01-preview', 'Full').identity.principalId - principalType: 'ServicePrincipal' - } -} - -resource mlKeyVault 'Microsoft.KeyVault/vaults@2019-09-01' = { - tags: tags - name: keyVault - location: location - properties: { - accessPolicies: [] - enableSoftDelete: true - softDeleteRetentionInDays: 7 - sku: { - family: 'A' - name: 'standard' - } - tenantId: subscription().tenantId - } -} - -resource mlAppInsights 'Microsoft.Insights/components@2020-02-02-preview' = { - tags: tags - name: appInsights - location: (((location == 'eastus2') || (location == 'westcentralus')) ? 'southcentralus' : location) - kind: 'web' - properties: { - Application_Type: 'web' - } -} - -resource azureML 'Microsoft.MachineLearningServices/workspaces@2020-06-01' = { - tags: tags - name: azureMLName - location: location - sku: { - name: azureMLSku - tier: azureMLSku - } - properties: { - applicationInsights: mlAppInsights.id - friendlyName: azureMLName - keyVault: mlKeyVault.id - storageAccount: storageAccount.id - // hbiWorkspace: hbiWorkspace - } - identity: { - type: 'SystemAssigned' - } -} diff --git a/modules/ml-synapse/metadata.json b/modules/ml-synapse/metadata.json deleted file mode 100644 index e62dccb8f7ab..000000000000 --- a/modules/ml-synapse/metadata.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "$schema": "https://aka.ms/azure-quickstart-templates-metadata-schema#", - "type": "Module", - "itemDisplayName": "Machine Learning with Synapse Workspace", - "description": "Azure Machine Learning and Synapse deployed with interconnected resources.", - "summary": "This template deploys an Azure Machine Learning, and Synapse Workspace. The common resources required for both are configured to connect and share common resources, such as the storage account and key vault", - "githubUsername": "dciborow", - "dateUpdated": "2022-03-25" -} From d3ccd80700a938d6a5d3fd6416dd8b388c79dbf0 Mon Sep 17 00:00:00 2001 From: Daniel Ciborowski Date: Mon, 4 Apr 2022 18:11:47 -0400 Subject: [PATCH 13/71] add params file --- .../gamedev-vm/azuredeploy.parameters.json | 15 ++++ .../gamedev-vm/gamedev-ama.bicep | 79 +++++++++++++++++++ 2 files changed, 94 insertions(+) create mode 100644 application-workloads/azure-gamedev/gamedev-vm/azuredeploy.parameters.json create mode 100644 application-workloads/azure-gamedev/gamedev-vm/gamedev-ama.bicep diff --git a/application-workloads/azure-gamedev/gamedev-vm/azuredeploy.parameters.json b/application-workloads/azure-gamedev/gamedev-vm/azuredeploy.parameters.json new file mode 100644 index 000000000000..37d742950974 --- /dev/null +++ b/application-workloads/azure-gamedev/gamedev-vm/azuredeploy.parameters.json @@ -0,0 +1,15 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "adminName": { + "value": "dcibad" + }, + "adminPass": { + "value": "B1g-D6taRocks" + }, + "vmSize": { + "value": "Standard_NV12s_v3" + } + } +} \ No newline at end of file diff --git a/application-workloads/azure-gamedev/gamedev-vm/gamedev-ama.bicep b/application-workloads/azure-gamedev/gamedev-vm/gamedev-ama.bicep new file mode 100644 index 000000000000..d7f0d656349c --- /dev/null +++ b/application-workloads/azure-gamedev/gamedev-vm/gamedev-ama.bicep @@ -0,0 +1,79 @@ + +param location string = resourceGroup().location + +@allowed([ + 'ue_4_27' + 'ue_5_0ea' +]) +param gameEngine string = 'ue_4_27' +@allowed([ + 'win10' + 'ws2019' +]) +param osType string = 'win10' + +@allowed([ + 'Standard_NC4as_T4_v3' + 'Standard_NC8as_T4_v3' + 'Standard_NC16as_T4_v3' + 'Standard_NC64as_T4_v3' + 'Standard_NV6' + 'Standard_NV12' + 'Standard_NV24' + 'Standard_NV12s_v3' + 'Standard_NV24s_v3' + 'Standard_NV48s_v3' +]) +param vmSize string = 'Standard_NV12s_v3' + +param administratorLogin string +@secure() +param passwordAdministratorLogin string + +@description('Remote Access technology') +@allowed([ + 'RDP' + 'Teradici' + 'Parsec' +]) +param remoteAccessTechnology string = 'RDP' + +param managedResourceGroupId string = '' + +resource solution_resource 'Microsoft.Solutions/applications@2017-09-01' = { + name : 'gamingDevVM' + location: location + kind : 'MarketPlace' + plan : { + 'name': 'game-dev-vm-amatest-plan' + 'product': 'f39e77ad-ada5-4145-9291-643e6e3ce1b2' + 'publisher': 'microsoftcorporation1602274591143' + 'version': '0.1.121' + } + properties: { + managedResourceGroupId: (empty(managedResourceGroupId) ? '${subscription().id}/resourceGroups/${take('${resourceGroup().name}-mrg', 90)}' : managedResourceGroupId) + parameters: { + location: { + value: location + } + vmSize: { + value: vmSize + } + adminName: { + value: administratorLogin + } + adminPass: { + value: passwordAdministratorLogin + } + osType: { + value: osType + } + gameEngine: { + value: gameEngine + } + remoteAccessTechnology: { + value: remoteAccessTechnology + } + } + } +} From 7962b86ec7c274b16d202f5f09e5e2ff6ace2737 Mon Sep 17 00:00:00 2001 From: Daniel Ciborowski Date: Mon, 4 Apr 2022 18:14:27 -0400 Subject: [PATCH 14/71] adding arm template --- .../azure-gamedev/gamedev-vm/main.json | 112 ++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 application-workloads/azure-gamedev/gamedev-vm/main.json diff --git a/application-workloads/azure-gamedev/gamedev-vm/main.json b/application-workloads/azure-gamedev/gamedev-vm/main.json new file mode 100644 index 000000000000..610912fd2e3f --- /dev/null +++ b/application-workloads/azure-gamedev/gamedev-vm/main.json @@ -0,0 +1,112 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.4.1318.3566", + "templateHash": "8393334568538788760" + } + }, + "parameters": { + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "gameEngine": { + "type": "string", + "defaultValue": "ue_4_27", + "allowedValues": [ + "ue_4_27", + "ue_5_0ea" + ] + }, + "osType": { + "type": "string", + "defaultValue": "win10", + "allowedValues": [ + "win10", + "ws2019" + ] + }, + "vmSize": { + "type": "string", + "defaultValue": "Standard_NV12s_v3", + "allowedValues": [ + "Standard_NC4as_T4_v3", + "Standard_NC8as_T4_v3", + "Standard_NC16as_T4_v3", + "Standard_NC64as_T4_v3", + "Standard_NV6", + "Standard_NV12", + "Standard_NV24", + "Standard_NV12s_v3", + "Standard_NV24s_v3", + "Standard_NV48s_v3" + ] + }, + "administratorLogin": { + "type": "string" + }, + "passwordAdministratorLogin": { + "type": "secureString" + }, + "remoteAccessTechnology": { + "type": "string", + "defaultValue": "RDP", + "allowedValues": [ + "RDP", + "Teradici", + "Parsec" + ], + "metadata": { + "description": "Remote Access technology" + } + }, + "managedResourceGroupId": { + "type": "string", + "defaultValue": "" + } + }, + "resources": [ + { + "type": "Microsoft.Solutions/applications", + "apiVersion": "2017-09-01", + "name": "gamingDevVM", + "location": "[parameters('location')]", + "kind": "MarketPlace", + "plan": { + "name": "game-dev-vm-amatest-plan", + "product": "f39e77ad-ada5-4145-9291-643e6e3ce1b2", + "publisher": "microsoftcorporation1602274591143", + "version": "0.1.121" + }, + "properties": { + "managedResourceGroupId": "[if(empty(parameters('managedResourceGroupId')), format('{0}/resourceGroups/{1}', subscription().id, take(format('{0}-mrg', resourceGroup().name), 90)), parameters('managedResourceGroupId'))]", + "parameters": { + "location": { + "value": "[parameters('location')]" + }, + "vmSize": { + "value": "[parameters('vmSize')]" + }, + "adminName": { + "value": "[parameters('administratorLogin')]" + }, + "adminPass": { + "value": "[parameters('passwordAdministratorLogin')]" + }, + "osType": { + "value": "[parameters('osType')]" + }, + "gameEngine": { + "value": "[parameters('gameEngine')]" + }, + "remoteAccessTechnology": { + "value": "[parameters('remoteAccessTechnology')]" + } + } + } + } + ] +} \ No newline at end of file From 78d55ed96313df3209b65e09525ce12f92b21de8 Mon Sep 17 00:00:00 2001 From: Daniel Ciborowski Date: Mon, 4 Apr 2022 18:28:03 -0400 Subject: [PATCH 15/71] restore bicep --- .../azure-gamedev/gamedev-vm/main.bicep | 45 +- .../azure-gamedev/gamedev-vm/main.json | 841 +++++++++++++++++- 2 files changed, 834 insertions(+), 52 deletions(-) diff --git a/application-workloads/azure-gamedev/gamedev-vm/main.bicep b/application-workloads/azure-gamedev/gamedev-vm/main.bicep index d7f0d656349c..4f7fe0f1c25d 100644 --- a/application-workloads/azure-gamedev/gamedev-vm/main.bicep +++ b/application-workloads/azure-gamedev/gamedev-vm/main.bicep @@ -38,42 +38,15 @@ param passwordAdministratorLogin string ]) param remoteAccessTechnology string = 'RDP' -param managedResourceGroupId string = '' - -resource solution_resource 'Microsoft.Solutions/applications@2017-09-01' = { +module gamedevvm 'gamedev-vm.bicep' = { name : 'gamingDevVM' - location: location - kind : 'MarketPlace' - plan : { - 'name': 'game-dev-vm-amatest-plan' - 'product': 'f39e77ad-ada5-4145-9291-643e6e3ce1b2' - 'publisher': 'microsoftcorporation1602274591143' - 'version': '0.1.121' - } - properties: { - managedResourceGroupId: (empty(managedResourceGroupId) ? '${subscription().id}/resourceGroups/${take('${resourceGroup().name}-mrg', 90)}' : managedResourceGroupId) - parameters: { - location: { - value: location - } - vmSize: { - value: vmSize - } - adminName: { - value: administratorLogin - } - adminPass: { - value: passwordAdministratorLogin - } - osType: { - value: osType - } - gameEngine: { - value: gameEngine - } - remoteAccessTechnology: { - value: remoteAccessTechnology - } - } + params: { + location: location + vmSize: vmSize + adminName: administratorLogin + adminPass: passwordAdministratorLogin + osType: osType + gameEngine: gameEngine + remoteAccessTechnology: remoteAccessTechnology } } diff --git a/application-workloads/azure-gamedev/gamedev-vm/main.json b/application-workloads/azure-gamedev/gamedev-vm/main.json index 610912fd2e3f..11fbbf4dd32c 100644 --- a/application-workloads/azure-gamedev/gamedev-vm/main.json +++ b/application-workloads/azure-gamedev/gamedev-vm/main.json @@ -5,7 +5,7 @@ "_generator": { "name": "bicep", "version": "0.4.1318.3566", - "templateHash": "8393334568538788760" + "templateHash": "291708570839268958" } }, "parameters": { @@ -62,27 +62,18 @@ "metadata": { "description": "Remote Access technology" } - }, - "managedResourceGroupId": { - "type": "string", - "defaultValue": "" } }, "resources": [ { - "type": "Microsoft.Solutions/applications", - "apiVersion": "2017-09-01", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2020-10-01", "name": "gamingDevVM", - "location": "[parameters('location')]", - "kind": "MarketPlace", - "plan": { - "name": "game-dev-vm-amatest-plan", - "product": "f39e77ad-ada5-4145-9291-643e6e3ce1b2", - "publisher": "microsoftcorporation1602274591143", - "version": "0.1.121" - }, "properties": { - "managedResourceGroupId": "[if(empty(parameters('managedResourceGroupId')), format('{0}/resourceGroups/{1}', subscription().id, take(format('{0}-mrg', resourceGroup().name), 90)), parameters('managedResourceGroupId'))]", + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", "parameters": { "location": { "value": "[parameters('location')]" @@ -105,6 +96,824 @@ "remoteAccessTechnology": { "value": "[parameters('remoteAccessTechnology')]" } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.4.1318.3566", + "templateHash": "1880175208089244772" + } + }, + "parameters": { + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Resource Location." + } + }, + "vmSize": { + "type": "string", + "defaultValue": "Standard_NV12s_v3", + "metadata": { + "description": "Virtual Machine Size." + } + }, + "vmName": { + "type": "string", + "defaultValue": "gamedevvm", + "metadata": { + "description": "Virtual Machine Name." + } + }, + "adminName": { + "type": "string", + "metadata": { + "description": "Virtual Machine User Name ." + } + }, + "adminPass": { + "type": "secureString", + "metadata": { + "type": "password", + "description": "Admin password." + } + }, + "osType": { + "type": "string", + "defaultValue": "win10", + "metadata": { + "description": "Operating System type" + } + }, + "gameEngine": { + "type": "string", + "defaultValue": "ue_4_27", + "allowedValues": [ + "ue_4_27", + "ue_5_0ea", + "unity_2020_3_19f1" + ], + "metadata": { + "description": "Game Engine" + } + }, + "gdkVersion": { + "type": "string", + "defaultValue": "June_2021_Update_4", + "metadata": { + "description": "GDK Version" + } + }, + "ibLicenseKey": { + "type": "secureString", + "defaultValue": "", + "metadata": { + "description": "Incredibuild License Key" + } + }, + "remoteAccessTechnology": { + "type": "string", + "defaultValue": "RDP", + "allowedValues": [ + "RDP", + "Teradici", + "Parsec" + ], + "metadata": { + "description": "Remote Access technology" + } + }, + "teradiciRegKey": { + "type": "secureString", + "defaultValue": "", + "metadata": { + "description": "Teradici Registration Key" + } + }, + "parsec_teamId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Parsec Team ID" + } + }, + "parsec_teamKey": { + "type": "secureString", + "defaultValue": "", + "metadata": { + "description": "Parsec Team Key" + } + }, + "parsec_host": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Parsec Hostname" + } + }, + "parsec_userEmail": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Parsec User Email" + } + }, + "parsec_isGuestAccess": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Parsec Is Guest Access" + } + }, + "numDataDisks": { + "type": "int", + "defaultValue": 0, + "metadata": { + "description": "Number of data disks" + } + }, + "dataDiskSize": { + "type": "int", + "defaultValue": 1024, + "metadata": { + "description": "Disk Performance Tier" + } + }, + "fileShareStorageAccount": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "File Share Storage Account name" + } + }, + "fileShareStorageAccountKey": { + "type": "secureString", + "defaultValue": "", + "metadata": { + "description": "File Share Storage Account key" + } + }, + "fileShareName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "File Share name" + } + }, + "p4Port": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Perforce Port address" + } + }, + "p4Username": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Perforce User" + } + }, + "p4Password": { + "type": "secureString", + "defaultValue": "", + "metadata": { + "description": "Perforce User password" + } + }, + "p4Workspace": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Perforce Client Workspace" + } + }, + "p4Stream": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Perforce Stream" + } + }, + "p4ClientViews": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Perforce Depot Client View mappings" + } + }, + "vnetName": { + "type": "string", + "defaultValue": "gamedev-vnet", + "metadata": { + "description": "Virtual Network name" + } + }, + "vnetARPrefixes": { + "type": "array", + "defaultValue": [ + "10.0.0.0/26" + ], + "metadata": { + "description": "Address prefix of the virtual network" + } + }, + "vnetNewOrExisting": { + "type": "string", + "defaultValue": "new", + "metadata": { + "description": "Virtual network is new or existing" + } + }, + "vnetRGName": { + "type": "string", + "defaultValue": "[resourceGroup().name]", + "metadata": { + "description": "Resource Group of the Virtual network" + } + }, + "subNetName": { + "type": "string", + "defaultValue": "gamedev-vnet-subnet1", + "metadata": { + "description": "VM Subnet name" + } + }, + "subNetARPrefix": { + "type": "string", + "defaultValue": "10.0.0.0/28", + "metadata": { + "description": "Subnet prefix of the virtual network" + } + }, + "publicIpName": { + "type": "string", + "defaultValue": "GameDevVM-IP", + "metadata": { + "description": "Unique public ip address name" + } + }, + "publicIpDns": { + "type": "string", + "defaultValue": "[format('gamedevvm{0}', uniqueString(resourceGroup().id))]", + "metadata": { + "description": "Unique DNS Public IP attached the VM" + } + }, + "publicIpAllocationMethod": { + "type": "string", + "defaultValue": "Dynamic", + "metadata": { + "description": "Public IP Allocoation Method" + } + }, + "publicIpSku": { + "type": "string", + "defaultValue": "Basic", + "allowedValues": [ + "Basic", + "Standard" + ], + "metadata": { + "description": "SKU number" + } + }, + "publicIpNewOrExisting": { + "type": "string", + "defaultValue": "new", + "metadata": { + "description": "Public IP New or Existing or None?" + } + }, + "publicIpRGName": { + "type": "string", + "defaultValue": "[resourceGroup().name]", + "metadata": { + "description": "Resource Group of the Public IP Address" + } + }, + "environment": { + "type": "string", + "defaultValue": "production", + "allowedValues": [ + "development", + "production" + ] + }, + "outTagsByResource": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Tags by resource." + } + }, + "_artifactsLocation": { + "type": "string", + "defaultValue": "[deployment().properties.templateLink.uri]", + "metadata": { + "description": "The base URI where artifacts required by this template are located including a trailing '/'" + } + }, + "_artifactsLocationSasToken": { + "type": "secureString", + "defaultValue": "", + "metadata": { + "description": "The sasToken required to access _artifactsLocation." + } + }, + "unrealPixelStreamingEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Enable or disable Unreal Pixel Streaming port." + } + }, + "enableManagedIdentity": { + "type": "bool", + "defaultValue": false + }, + "enableAAD": { + "type": "bool", + "defaultValue": false + } + }, + "variables": { + "environmentMapping": { + "ue_4_27": "unreal_4_27", + "ue_5_0ea": "unreal_5_0ea", + "unity_2020_3_19f1": "unity_2020_3_19f1" + }, + "environments": { + "development": { + "vmImage": { + "publisher": "microsoft-agci-gaming", + "offer": "agci-gamedev-image", + "sku": "[format('gamedev-{0}-{1}', parameters('gameEngine'), parameters('osType'))]", + "version": "latest" + }, + "vmPlan": { + "publisher": "microsoft-agci-gaming", + "product": "agci-gamedev-image", + "name": "[format('gamedev-{0}-{1}', parameters('gameEngine'), parameters('osType'))]" + } + }, + "production": { + "vmImage": { + "publisher": "microsoftcorporation1602274591143", + "offer": "game-dev-vm", + "sku": "[format('{0}_{1}', parameters('osType'), variables('environmentMapping')[parameters('gameEngine')])]", + "version": "latest" + }, + "vmPlan": { + "publisher": "microsoftcorporation1602274591143", + "product": "game-dev-vm", + "name": "[format('{0}_{1}', parameters('osType'), variables('environmentMapping')[parameters('gameEngine')])]" + } + } + }, + "vmImage": "[variables('environments')[parameters('environment')].vmImage]", + "vmPlan": "[variables('environments')[parameters('environment')].vmPlan]", + "vmName_var": "[parameters('vmName')]", + "ipconfName": "[format('{0}-ipconf', variables('vmName_var'))]", + "nicName_var": "[format('{0}-nic', variables('vmName_var'))]", + "nsgName_var": "[format('{0}-nsg', variables('vmName_var'))]", + "storageType_var": "[if(bool(greater(length(split(parameters('vmSize'), '_')), 2)), 'Premium_LRS', 'Standard_LRS')]", + "tags_var": { + "solution": "Game Development Virtual Machine", + "engine": "[parameters('gameEngine')]", + "ostype": "[parameters('osType')]", + "remotesoftware": "[parameters('remoteAccessTechnology')]" + }, + "cmdGDKInstall": "[format('(Get-Content ''C:\\ProgramData\\Microsoft\\Windows\\Start Menu\\Programs\\Startup\\gdkinstall.cmd'').replace(''[VERSION]'', ''{0}'') | Set-Content ''C:\\ProgramData\\Microsoft\\Windows\\Start Menu\\Programs\\Startup\\gdkinstall.cmd''', parameters('gdkVersion'))]", + "userData": "[format('team_id={0}:key={1}:name={2}:user_email={3}:is_guest_access={4}:ibLicenseKey={5}', parameters('parsec_teamId'), parameters('parsec_teamKey'), parameters('parsec_host'), parameters('parsec_userEmail'), parameters('parsec_isGuestAccess'), parameters('ibLicenseKey'))]", + "Script2Run": "TeradiciRegCAS.ps1", + "CSEParams": "[format(' -pcoip_registration_code {0}', parameters('teradiciRegKey'))]", + "cmdTeradiciRegistration": "[format('./{0}{1}', variables('Script2Run'), variables('CSEParams'))]", + "vnetId": { + "new": "[resourceId('Microsoft.Network/virtualNetworks', parameters('vnetName'))]", + "existing": "[resourceId(parameters('vnetRGName'), 'Microsoft.Network/virtualNetworks', parameters('vnetName'))]" + }, + "subnetId": "[format('{0}/subnets/{1}', variables('vnetId')[parameters('vnetNewOrExisting')], parameters('subNetName'))]", + "publicIpId": "[createObject('new', resourceId('Microsoft.Network/publicIPAddresses', parameters('publicIpName')), 'existing', resourceId(parameters('publicIpRGName'), 'Microsoft.Network/publicIPAddresses', parameters('publicIpName')), 'none', '')[parameters('publicIpNewOrExisting')]]" + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2020-06-01", + "name": "pid-7837dd60-4ba8-419a-a26f-237bbe170773-partnercenter", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [] + } + } + }, + { + "condition": "[equals(parameters('publicIpNewOrExisting'), 'new')]", + "type": "Microsoft.Network/publicIPAddresses", + "apiVersion": "2021-03-01", + "name": "[parameters('publicIpName')]", + "sku": { + "name": "[parameters('publicIpSku')]" + }, + "location": "[parameters('location')]", + "properties": { + "publicIPAllocationMethod": "[parameters('publicIpAllocationMethod')]", + "dnsSettings": { + "domainNameLabel": "[parameters('publicIpDns')]" + } + } + }, + { + "type": "Microsoft.Network/networkSecurityGroups", + "apiVersion": "2021-03-01", + "name": "[variables('nsgName_var')]", + "location": "[parameters('location')]", + "properties": { + "securityRules": "[reference(resourceId('Microsoft.Resources/deployments', 'nsg_rules')).outputs.nsgRules.value[format('nsgRules-{0}', parameters('remoteAccessTechnology'))]]" + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'nsg_rules')]" + ] + }, + { + "condition": "[equals(parameters('vnetNewOrExisting'), 'new')]", + "type": "Microsoft.Network/virtualNetworks", + "apiVersion": "2021-03-01", + "name": "[parameters('vnetName')]", + "location": "[parameters('location')]", + "properties": { + "addressSpace": { + "addressPrefixes": [ + "[first(parameters('vnetARPrefixes'))]" + ] + }, + "subnets": [ + { + "name": "[parameters('subNetName')]", + "properties": { + "addressPrefix": "[parameters('subNetARPrefix')]" + } + } + ] + } + }, + { + "type": "Microsoft.Network/networkInterfaces", + "apiVersion": "2021-03-01", + "name": "[variables('nicName_var')]", + "location": "[parameters('location')]", + "properties": { + "enableAcceleratedNetworking": "[if(bool(greater(length(split(parameters('vmSize'), '_')), 2)), true(), false())]", + "ipConfigurations": [ + { + "name": "[variables('ipconfName')]", + "properties": "[union(createObject('subnet', createObject('id', variables('subnetId'))), createObject('privateIPAllocationMethod', 'Dynamic'), if(not(empty(variables('publicIpId'))), createObject('publicIPAddress', createObject('id', variables('publicIpId'))), createObject()))]" + } + ], + "networkSecurityGroup": { + "id": "[resourceId('Microsoft.Network/networkSecurityGroups', variables('nsgName_var'))]" + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Network/networkSecurityGroups', variables('nsgName_var'))]", + "[resourceId('Microsoft.Network/virtualNetworks', parameters('vnetName'))]" + ] + }, + { + "type": "Microsoft.Compute/virtualMachines", + "apiVersion": "2021-03-01", + "name": "[variables('vmName_var')]", + "location": "[parameters('location')]", + "plan": "[variables('vmPlan')]", + "identity": "[if(or(parameters('enableAAD'), parameters('enableManagedIdentity')), createObject('type', 'systemAssigned'), null())]", + "properties": { + "hardwareProfile": { + "vmSize": "[parameters('vmSize')]" + }, + "storageProfile": { + "copy": [ + { + "name": "dataDisks", + "count": "[length(range(0, parameters('numDataDisks')))]", + "input": { + "lun": "[range(0, parameters('numDataDisks'))[copyIndex('dataDisks')]]", + "createOption": "Empty", + "diskSizeGB": "[parameters('dataDiskSize')]" + } + } + ], + "imageReference": "[variables('vmImage')]", + "osDisk": { + "name": "[format('{0}-osdisk', variables('vmName_var'))]", + "createOption": "FromImage", + "caching": "ReadWrite", + "diskSizeGB": 255, + "managedDisk": { + "storageAccountType": "[variables('storageType_var')]" + } + } + }, + "osProfile": { + "computerName": "[variables('vmName_var')]", + "adminUsername": "[parameters('adminName')]", + "adminPassword": "[parameters('adminPass')]" + }, + "userData": "[base64(variables('userData'))]", + "networkProfile": { + "networkInterfaces": [ + { + "id": "[resourceId('Microsoft.Network/networkInterfaces', variables('nicName_var'))]" + } + ] + } + }, + "tags": "[if(contains(parameters('outTagsByResource'), 'Microsoft.Compute/virtualMachines'), union(variables('tags_var'), parameters('outTagsByResource')['Microsoft.Compute/virtualMachines']), variables('tags_var'))]", + "dependsOn": [ + "[resourceId('Microsoft.Network/networkInterfaces', variables('nicName_var'))]" + ] + }, + { + "condition": "[parameters('enableAAD')]", + "type": "Microsoft.Compute/virtualMachines/extensions", + "apiVersion": "2019-12-01", + "name": "[format('{0}/AADLoginForWindows', variables('vmName_var'))]", + "location": "[parameters('location')]", + "properties": { + "publisher": "Microsoft.Azure.ActiveDirectory", + "type": "AADLoginForWindows", + "typeHandlerVersion": "1.0", + "autoUpgradeMinorVersion": true + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'runRemoteAccess')]", + "[resourceId('Microsoft.Compute/virtualMachines', variables('vmName_var'))]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2020-10-01", + "name": "nsg_rules", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "addPixelStreamingPorts": { + "value": "[parameters('unrealPixelStreamingEnabled')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.4.1318.3566", + "templateHash": "11526155460257618909" + } + }, + "parameters": { + "addPixelStreamingPorts": { + "type": "bool", + "defaultValue": false + } + }, + "variables": { + "nsgRules": { + "nsgRules-RDP": "[if(not(parameters('addPixelStreamingPorts')), createArray(createObject('name', 'RDP', 'properties', createObject('priority', 1010, 'protocol', '*', 'access', 'Allow', 'direction', 'Inbound', 'sourceAddressPrefix', '*', 'sourcePortRange', '*', 'destinationAddressPrefix', '*', 'destinationPortRange', '3389'))), createArray(createObject('name', 'RDP', 'properties', createObject('priority', 1010, 'protocol', '*', 'access', 'Allow', 'direction', 'Inbound', 'sourceAddressPrefix', '*', 'sourcePortRange', '*', 'destinationAddressPrefix', '*', 'destinationPortRange', '3389')), createObject('name', 'PixelStream', 'properties', createObject('priority', 1020, 'protocol', '*', 'access', 'Allow', 'direction', 'Inbound', 'sourceAddressPrefix', '*', 'sourcePortRange', '*', 'destinationAddressPrefix', '*', 'destinationPortRange', '80'))))]", + "nsgRules-Teradici": "[if(not(parameters('addPixelStreamingPorts')), createArray(createObject('name', 'RDP', 'properties', createObject('priority', 1010, 'protocol', '*', 'access', 'Allow', 'direction', 'Inbound', 'sourceAddressPrefix', '*', 'sourcePortRange', '*', 'destinationAddressPrefix', '*', 'destinationPortRange', '3389')), createObject('name', 'PCoIPtcp', 'properties', createObject('priority', 1020, 'protocol', 'TCP', 'access', 'Allow', 'direction', 'Inbound', 'sourceAddressPrefix', '*', 'sourcePortRange', '*', 'destinationAddressPrefix', '*', 'destinationPortRange', '4172')), createObject('name', 'PCoIPudp', 'properties', createObject('priority', 1030, 'protocol', 'UDP', 'access', 'Allow', 'direction', 'Inbound', 'sourceAddressPrefix', '*', 'sourcePortRange', '*', 'destinationAddressPrefix', '*', 'destinationPortRange', '4172')), createObject('name', 'CertAuthHTTPS', 'properties', createObject('priority', 1040, 'protocol', 'TCP', 'access', 'Allow', 'direction', 'Inbound', 'sourceAddressPrefix', '*', 'sourcePortRange', '*', 'destinationAddressPrefix', '*', 'destinationPortRange', '443')), createObject('name', 'TeradiciCom', 'properties', createObject('priority', 1050, 'protocol', 'TCP', 'access', 'Allow', 'direction', 'Inbound', 'sourceAddressPrefix', '*', 'sourcePortRange', '*', 'destinationAddressPrefix', '*', 'destinationPortRange', '60443'))), createArray(createObject('name', 'RDP', 'properties', createObject('priority', 1010, 'protocol', '*', 'access', 'Allow', 'direction', 'Inbound', 'sourceAddressPrefix', '*', 'sourcePortRange', '*', 'destinationAddressPrefix', '*', 'destinationPortRange', '3389')), createObject('name', 'PCoIPtcp', 'properties', createObject('priority', 1020, 'protocol', 'TCP', 'access', 'Allow', 'direction', 'Inbound', 'sourceAddressPrefix', '*', 'sourcePortRange', '*', 'destinationAddressPrefix', '*', 'destinationPortRange', '4172')), createObject('name', 'PCoIPudp', 'properties', createObject('priority', 1030, 'protocol', 'UDP', 'access', 'Allow', 'direction', 'Inbound', 'sourceAddressPrefix', '*', 'sourcePortRange', '*', 'destinationAddressPrefix', '*', 'destinationPortRange', '4172')), createObject('name', 'CertAuthHTTPS', 'properties', createObject('priority', 1040, 'protocol', 'TCP', 'access', 'Allow', 'direction', 'Inbound', 'sourceAddressPrefix', '*', 'sourcePortRange', '*', 'destinationAddressPrefix', '*', 'destinationPortRange', '443')), createObject('name', 'TeradiciCom', 'properties', createObject('priority', 1050, 'protocol', 'TCP', 'access', 'Allow', 'direction', 'Inbound', 'sourceAddressPrefix', '*', 'sourcePortRange', '*', 'destinationAddressPrefix', '*', 'destinationPortRange', '60443')), createObject('name', 'PixelStream', 'properties', createObject('priority', 1060, 'protocol', '*', 'access', 'Allow', 'direction', 'Inbound', 'sourceAddressPrefix', '*', 'sourcePortRange', '*', 'destinationAddressPrefix', '*', 'destinationPortRange', '80'))))]", + "nsgRules-Parsec": "[if(not(parameters('addPixelStreamingPorts')), createArray(createObject('name', 'RDP', 'properties', createObject('priority', 1010, 'protocol', '*', 'access', 'Allow', 'direction', 'Inbound', 'sourceAddressPrefix', '*', 'sourcePortRange', '*', 'destinationAddressPrefix', '*', 'destinationPortRange', '3389'))), createArray(createObject('name', 'RDP', 'properties', createObject('priority', 1010, 'protocol', '*', 'access', 'Allow', 'direction', 'Inbound', 'sourceAddressPrefix', '*', 'sourcePortRange', '*', 'destinationAddressPrefix', '*', 'destinationPortRange', '3389')), createObject('name', 'PixelStream', 'properties', createObject('priority', 1020, 'protocol', '*', 'access', 'Allow', 'direction', 'Inbound', 'sourceAddressPrefix', '*', 'sourcePortRange', '*', 'destinationAddressPrefix', '*', 'destinationPortRange', '80'))))]" + } + }, + "resources": [], + "outputs": { + "nsgRules": { + "type": "object", + "value": "[variables('nsgRules')]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2020-10-01", + "name": "runRemoteAccess", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "cmdGDKInstall": { + "value": "[variables('cmdGDKInstall')]" + }, + "cmdTeradiciRegistration": { + "value": "[variables('cmdTeradiciRegistration')]" + }, + "_artifactsLocation": { + "value": "[parameters('_artifactsLocation')]" + }, + "_artifactsLocationSasToken": { + "value": "[parameters('_artifactsLocationSasToken')]" + }, + "virtualMachineName": { + "value": "[variables('vmName_var')]" + }, + "remoteAccessTechnology": { + "value": "[parameters('remoteAccessTechnology')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "fileShareStorageAccount": { + "value": "[parameters('fileShareStorageAccount')]" + }, + "fileShareStorageAccountKey": { + "value": "[parameters('fileShareStorageAccountKey')]" + }, + "fileShareName": { + "value": "[parameters('fileShareName')]" + }, + "p4Port": { + "value": "[parameters('p4Port')]" + }, + "p4Username": { + "value": "[parameters('p4Username')]" + }, + "p4Password": { + "value": "[parameters('p4Password')]" + }, + "p4Workspace": { + "value": "[parameters('p4Workspace')]" + }, + "p4Stream": { + "value": "[parameters('p4Stream')]" + }, + "p4ClientViews": { + "value": "[parameters('p4ClientViews')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.4.1318.3566", + "templateHash": "12415608506796335919" + } + }, + "parameters": { + "cmdGDKInstall": { + "type": "string" + }, + "cmdTeradiciRegistration": { + "type": "string" + }, + "_artifactsLocation": { + "type": "string" + }, + "_artifactsLocationSasToken": { + "type": "string" + }, + "virtualMachineName": { + "type": "string" + }, + "remoteAccessTechnology": { + "type": "string" + }, + "location": { + "type": "string" + }, + "fileShareStorageAccount": { + "type": "string" + }, + "fileShareStorageAccountKey": { + "type": "string" + }, + "fileShareName": { + "type": "string" + }, + "p4Port": { + "type": "string" + }, + "p4Username": { + "type": "string" + }, + "p4Password": { + "type": "string" + }, + "p4Workspace": { + "type": "string" + }, + "p4Stream": { + "type": "string" + }, + "p4ClientViews": { + "type": "string" + } + }, + "variables": { + "mountFileShareParams": "[format('-storageAccount ''{0}'' -storageAccountKey ''{1}'' -fileShareName ''{2}''', parameters('fileShareStorageAccount'), parameters('fileShareStorageAccountKey'), parameters('fileShareName'))]", + "p4Params": "[format('-p4Port ''{0}'' -p4Username ''{1}'' -p4Password ''{2}'' -p4Workspace ''{3}'' -p4Stream ''{4}'' -p4ClientViews ''{5}''', parameters('p4Port'), parameters('p4Username'), parameters('p4Password'), parameters('p4Workspace'), parameters('p4Stream'), parameters('p4ClientViews'))]", + "remoteAccessExtension": { + "RDP": { + "publisher": "Microsoft.Compute", + "type": "CustomScriptExtension", + "typeHandlerVersion": "1.10", + "autoUpgradeMinorVersion": true, + "settings": { + "fileUris": [ + "[uri(parameters('_artifactsLocation'), format('CreateDataDisk.ps1{0}', parameters('_artifactsLocationSasToken')))]", + "[uri(parameters('_artifactsLocation'), format('MountFileShare.ps1{0}', parameters('_artifactsLocationSasToken')))]", + "[uri(parameters('_artifactsLocation'), format('p4DepotSync.ps1{0}', parameters('_artifactsLocationSasToken')))]", + "[uri(parameters('_artifactsLocation'), format('ibSetup.ps1{0}', parameters('_artifactsLocationSasToken')))]" + ], + "commandToExecute": "[format('powershell -ExecutionPolicy Unrestricted -NoProfile -NonInteractive -command \"./CreateDataDisk.ps1;./MountFileShare.ps1 {0};./p4DepotSync.ps1 {1};./ibSetup.ps1;{2}\"', variables('mountFileShareParams'), variables('p4Params'), parameters('cmdGDKInstall'))]" + } + }, + "Teradici": { + "publisher": "Microsoft.Compute", + "type": "CustomScriptExtension", + "typeHandlerVersion": "1.10", + "autoUpgradeMinorVersion": true, + "settings": { + "fileUris": [ + "[uri(parameters('_artifactsLocation'), format('CreateDataDisk.ps1{0}', parameters('_artifactsLocationSasToken')))]", + "[uri(parameters('_artifactsLocation'), format('MountFileShare.ps1{0}', parameters('_artifactsLocationSasToken')))]", + "[uri(parameters('_artifactsLocation'), format('p4DepotSync.ps1{0}', parameters('_artifactsLocationSasToken')))]", + "[uri(parameters('_artifactsLocation'), format('ibSetup.ps1{0}', parameters('_artifactsLocationSasToken')))]", + "[uri(parameters('_artifactsLocation'), format('TeradiciRegCAS.ps1{0}', parameters('_artifactsLocationSasToken')))]" + ] + }, + "protectedSettings": { + "commandToExecute": "[format('powershell -ExecutionPolicy Unrestricted -command \"./CreateDataDisk.ps1;./MountFileShare.ps1 {0};./p4DepotSync.ps1 {1};./ibSetup.ps1;{2};{3}\"', variables('mountFileShareParams'), variables('p4Params'), parameters('cmdGDKInstall'), parameters('cmdTeradiciRegistration'))]" + } + }, + "Parsec": { + "publisher": "Microsoft.Compute", + "type": "CustomScriptExtension", + "typeHandlerVersion": "1.10", + "autoUpgradeMinorVersion": true, + "settings": { + "fileUris": [ + "[uri(parameters('_artifactsLocation'), format('CreateDataDisk.ps1{0}', parameters('_artifactsLocationSasToken')))]", + "[uri(parameters('_artifactsLocation'), format('MountFileShare.ps1{0}', parameters('_artifactsLocationSasToken')))]", + "[uri(parameters('_artifactsLocation'), format('p4DepotSync.ps1{0}', parameters('_artifactsLocationSasToken')))]", + "[uri(parameters('_artifactsLocation'), format('ibSetup.ps1{0}', parameters('_artifactsLocationSasToken')))]", + "[uri(parameters('_artifactsLocation'), format('PostInstall.ps1{0}', parameters('_artifactsLocationSasToken')))]", + "[uri(parameters('_artifactsLocation'), format('PreInstall.zip{0}', parameters('_artifactsLocationSasToken')))]" + ], + "commandToExecute": "[format('powershell -ExecutionPolicy Unrestricted -NoProfile -NonInteractive -command \"./CreateDataDisk.ps1;./MountFileShare.ps1 {0};./p4DepotSync.ps1 {1};./ibSetup.ps1;{2};./PostInstall.ps1\"', variables('mountFileShareParams'), variables('p4Params'), parameters('cmdGDKInstall'))]" + } + } + } + }, + "resources": [ + { + "type": "Microsoft.Compute/virtualMachines/extensions", + "apiVersion": "2019-12-01", + "name": "[format('{0}/CustomScriptExtension-{1}', parameters('virtualMachineName'), parameters('remoteAccessTechnology'))]", + "location": "[parameters('location')]", + "properties": "[variables('remoteAccessExtension')[parameters('remoteAccessTechnology')]]" + } + ], + "outputs": { + "id": { + "type": "string", + "value": "[resourceId('Microsoft.Compute/virtualMachines/extensions', split(format('{0}/CustomScriptExtension-{1}', parameters('virtualMachineName'), parameters('remoteAccessTechnology')), '/')[0], split(format('{0}/CustomScriptExtension-{1}', parameters('virtualMachineName'), parameters('remoteAccessTechnology')), '/')[1])]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Compute/virtualMachines', variables('vmName_var'))]" + ] + } + ], + "outputs": { + "Host_Name": { + "type": "string", + "value": "[if(not(empty(variables('publicIpId'))), reference(variables('publicIpId'), '2021-03-01').dnsSettings.fqdn, '')]" + }, + "UserName": { + "type": "string", + "value": "[parameters('adminName')]" + }, + "IPAddress": { + "type": "string", + "value": "[if(not(empty(variables('publicIpId'))), variables('publicIpId'), '')]" + } + } } } } From ba71d3d0114c24ec310df4bc22ed7acbd5480b4b Mon Sep 17 00:00:00 2001 From: Daniel Ciborowski Date: Mon, 4 Apr 2022 18:33:07 -0400 Subject: [PATCH 16/71] Update gamedev-vm.bicep --- application-workloads/azure-gamedev/gamedev-vm/gamedev-vm.bicep | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application-workloads/azure-gamedev/gamedev-vm/gamedev-vm.bicep b/application-workloads/azure-gamedev/gamedev-vm/gamedev-vm.bicep index 24d84deae057..7923fb578eff 100644 --- a/application-workloads/azure-gamedev/gamedev-vm/gamedev-vm.bicep +++ b/application-workloads/azure-gamedev/gamedev-vm/gamedev-vm.bicep @@ -150,7 +150,7 @@ param environment string = 'production' param outTagsByResource object = {} @description('The base URI where artifacts required by this template are located including a trailing \'/\'') -param _artifactsLocation string = deployment().properties.templateLink.uri +param _artifactsLocation string = 'https://raw.githubusercontent.com/dciborow/azure-quickstart-templates/dciborow/gamedev-vm/application-workloads/azure-gamedev/gamedev-vm/' @description('The sasToken required to access _artifactsLocation.') @secure() From 8d1998d8d1e46b7c10bf45c2396b5a4f46300e75 Mon Sep 17 00:00:00 2001 From: Daniel Ciborowski Date: Mon, 4 Apr 2022 18:35:32 -0400 Subject: [PATCH 17/71] github link --- application-workloads/azure-gamedev/gamedev-vm/main.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/application-workloads/azure-gamedev/gamedev-vm/main.json b/application-workloads/azure-gamedev/gamedev-vm/main.json index 11fbbf4dd32c..0deea5b0da2e 100644 --- a/application-workloads/azure-gamedev/gamedev-vm/main.json +++ b/application-workloads/azure-gamedev/gamedev-vm/main.json @@ -5,7 +5,7 @@ "_generator": { "name": "bicep", "version": "0.4.1318.3566", - "templateHash": "291708570839268958" + "templateHash": "11544900663634062482" } }, "parameters": { @@ -104,7 +104,7 @@ "_generator": { "name": "bicep", "version": "0.4.1318.3566", - "templateHash": "1880175208089244772" + "templateHash": "17462343708268683319" } }, "parameters": { @@ -413,7 +413,7 @@ }, "_artifactsLocation": { "type": "string", - "defaultValue": "[deployment().properties.templateLink.uri]", + "defaultValue": "https://raw.githubusercontent.com/dciborow/azure-quickstart-templates/dciborow/gamedev-vm/application-workloads/azure-gamedev/gamedev-vm/", "metadata": { "description": "The base URI where artifacts required by this template are located including a trailing '/'" } From 68c6ffc51652a0a2bd694507797f8091a4c37fd0 Mon Sep 17 00:00:00 2001 From: Daniel Ciborowski Date: Mon, 4 Apr 2022 19:16:10 -0400 Subject: [PATCH 18/71] use public blob --- application-workloads/azure-gamedev/gamedev-vm/main.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/application-workloads/azure-gamedev/gamedev-vm/main.json b/application-workloads/azure-gamedev/gamedev-vm/main.json index 0deea5b0da2e..f0d6b89115d3 100644 --- a/application-workloads/azure-gamedev/gamedev-vm/main.json +++ b/application-workloads/azure-gamedev/gamedev-vm/main.json @@ -5,7 +5,7 @@ "_generator": { "name": "bicep", "version": "0.4.1318.3566", - "templateHash": "11544900663634062482" + "templateHash": "2190319362431488956" } }, "parameters": { @@ -104,7 +104,7 @@ "_generator": { "name": "bicep", "version": "0.4.1318.3566", - "templateHash": "17462343708268683319" + "templateHash": "13635287766863181236" } }, "parameters": { @@ -413,7 +413,7 @@ }, "_artifactsLocation": { "type": "string", - "defaultValue": "https://raw.githubusercontent.com/dciborow/azure-quickstart-templates/dciborow/gamedev-vm/application-workloads/azure-gamedev/gamedev-vm/", + "defaultValue": "https://dciborowmmlsparkstore.blob.core.windows.net/mmlspark/", "metadata": { "description": "The base URI where artifacts required by this template are located including a trailing '/'" } From 73f7e82218c499d671a0b7c4013d21a9f0534265 Mon Sep 17 00:00:00 2001 From: Daniel Ciborowski Date: Tue, 12 Apr 2022 19:05:36 -0400 Subject: [PATCH 19/71] reformat files to standard --- .../{main.json => azuredeploy.json} | 48 +++++------ .../gamedev-vm/azuredeploy.parameters.json | 8 +- .../gamedev-vm/gamedev-ama.bicep | 79 ------------------- .../azure-gamedev/gamedev-vm/main.bicep | 2 +- .../{ => nestedtemplates}/gamedev-vm.bicep | 2 +- .../{ => nestedtemplates}/nsgRules.bicep | 0 .../remoteAccessExtension.bicep | 30 +++---- .../{ => scripts}/MountFIleShare.ps1 | 0 .../{ => scripts}/TeradiciRegCAS.ps1 | 0 .../{ => scripts}/createDataDisk.ps1 | 0 .../gamedev-vm/{ => scripts}/ibSetup.ps1 | 0 .../gamedev-vm/{ => scripts}/p4DepotSync.ps1 | 0 12 files changed, 45 insertions(+), 124 deletions(-) rename application-workloads/azure-gamedev/gamedev-vm/{main.json => azuredeploy.json} (95%) delete mode 100644 application-workloads/azure-gamedev/gamedev-vm/gamedev-ama.bicep rename application-workloads/azure-gamedev/gamedev-vm/{ => nestedtemplates}/gamedev-vm.bicep (98%) rename application-workloads/azure-gamedev/gamedev-vm/{ => nestedtemplates}/nsgRules.bicep (100%) rename application-workloads/azure-gamedev/gamedev-vm/{ => nestedtemplates}/remoteAccessExtension.bicep (68%) rename application-workloads/azure-gamedev/gamedev-vm/{ => scripts}/MountFIleShare.ps1 (100%) rename application-workloads/azure-gamedev/gamedev-vm/{ => scripts}/TeradiciRegCAS.ps1 (100%) rename application-workloads/azure-gamedev/gamedev-vm/{ => scripts}/createDataDisk.ps1 (100%) rename application-workloads/azure-gamedev/gamedev-vm/{ => scripts}/ibSetup.ps1 (100%) rename application-workloads/azure-gamedev/gamedev-vm/{ => scripts}/p4DepotSync.ps1 (100%) diff --git a/application-workloads/azure-gamedev/gamedev-vm/main.json b/application-workloads/azure-gamedev/gamedev-vm/azuredeploy.json similarity index 95% rename from application-workloads/azure-gamedev/gamedev-vm/main.json rename to application-workloads/azure-gamedev/gamedev-vm/azuredeploy.json index f0d6b89115d3..671665984afa 100644 --- a/application-workloads/azure-gamedev/gamedev-vm/main.json +++ b/application-workloads/azure-gamedev/gamedev-vm/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.4.1318.3566", - "templateHash": "2190319362431488956" + "version": "0.5.6.12127", + "templateHash": "14032160832736697226" } }, "parameters": { @@ -103,8 +103,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.4.1318.3566", - "templateHash": "13635287766863181236" + "version": "0.5.6.12127", + "templateHash": "4432448743094339894" } }, "parameters": { @@ -413,7 +413,7 @@ }, "_artifactsLocation": { "type": "string", - "defaultValue": "https://dciborowmmlsparkstore.blob.core.windows.net/mmlspark/", + "defaultValue": "", "metadata": { "description": "The base URI where artifacts required by this template are located including a trailing '/'" } @@ -676,8 +676,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.4.1318.3566", - "templateHash": "11526155460257618909" + "version": "0.5.6.12127", + "templateHash": "16879828913748412764" } }, "parameters": { @@ -768,8 +768,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.4.1318.3566", - "templateHash": "12415608506796335919" + "version": "0.5.6.12127", + "templateHash": "1649725417760946162" } }, "parameters": { @@ -833,10 +833,10 @@ "autoUpgradeMinorVersion": true, "settings": { "fileUris": [ - "[uri(parameters('_artifactsLocation'), format('CreateDataDisk.ps1{0}', parameters('_artifactsLocationSasToken')))]", - "[uri(parameters('_artifactsLocation'), format('MountFileShare.ps1{0}', parameters('_artifactsLocationSasToken')))]", - "[uri(parameters('_artifactsLocation'), format('p4DepotSync.ps1{0}', parameters('_artifactsLocationSasToken')))]", - "[uri(parameters('_artifactsLocation'), format('ibSetup.ps1{0}', parameters('_artifactsLocationSasToken')))]" + "[uri(parameters('_artifactsLocation'), format('scripts/CreateDataDisk.ps1{0}', parameters('_artifactsLocationSasToken')))]", + "[uri(parameters('_artifactsLocation'), format('scripts/MountFileShare.ps1{0}', parameters('_artifactsLocationSasToken')))]", + "[uri(parameters('_artifactsLocation'), format('scripts/p4DepotSync.ps1{0}', parameters('_artifactsLocationSasToken')))]", + "[uri(parameters('_artifactsLocation'), format('scripts/ibSetup.ps1{0}', parameters('_artifactsLocationSasToken')))]" ], "commandToExecute": "[format('powershell -ExecutionPolicy Unrestricted -NoProfile -NonInteractive -command \"./CreateDataDisk.ps1;./MountFileShare.ps1 {0};./p4DepotSync.ps1 {1};./ibSetup.ps1;{2}\"', variables('mountFileShareParams'), variables('p4Params'), parameters('cmdGDKInstall'))]" } @@ -848,11 +848,11 @@ "autoUpgradeMinorVersion": true, "settings": { "fileUris": [ - "[uri(parameters('_artifactsLocation'), format('CreateDataDisk.ps1{0}', parameters('_artifactsLocationSasToken')))]", - "[uri(parameters('_artifactsLocation'), format('MountFileShare.ps1{0}', parameters('_artifactsLocationSasToken')))]", - "[uri(parameters('_artifactsLocation'), format('p4DepotSync.ps1{0}', parameters('_artifactsLocationSasToken')))]", - "[uri(parameters('_artifactsLocation'), format('ibSetup.ps1{0}', parameters('_artifactsLocationSasToken')))]", - "[uri(parameters('_artifactsLocation'), format('TeradiciRegCAS.ps1{0}', parameters('_artifactsLocationSasToken')))]" + "[uri(parameters('_artifactsLocation'), format('scripts/CreateDataDisk.ps1{0}', parameters('_artifactsLocationSasToken')))]", + "[uri(parameters('_artifactsLocation'), format('scripts/MountFileShare.ps1{0}', parameters('_artifactsLocationSasToken')))]", + "[uri(parameters('_artifactsLocation'), format('scripts/p4DepotSync.ps1{0}', parameters('_artifactsLocationSasToken')))]", + "[uri(parameters('_artifactsLocation'), format('scripts/ibSetup.ps1{0}', parameters('_artifactsLocationSasToken')))]", + "[uri(parameters('_artifactsLocation'), format('scripts/TeradiciRegCAS.ps1{0}', parameters('_artifactsLocationSasToken')))]" ] }, "protectedSettings": { @@ -866,12 +866,12 @@ "autoUpgradeMinorVersion": true, "settings": { "fileUris": [ - "[uri(parameters('_artifactsLocation'), format('CreateDataDisk.ps1{0}', parameters('_artifactsLocationSasToken')))]", - "[uri(parameters('_artifactsLocation'), format('MountFileShare.ps1{0}', parameters('_artifactsLocationSasToken')))]", - "[uri(parameters('_artifactsLocation'), format('p4DepotSync.ps1{0}', parameters('_artifactsLocationSasToken')))]", - "[uri(parameters('_artifactsLocation'), format('ibSetup.ps1{0}', parameters('_artifactsLocationSasToken')))]", - "[uri(parameters('_artifactsLocation'), format('PostInstall.ps1{0}', parameters('_artifactsLocationSasToken')))]", - "[uri(parameters('_artifactsLocation'), format('PreInstall.zip{0}', parameters('_artifactsLocationSasToken')))]" + "[uri(parameters('_artifactsLocation'), format('scripts/CreateDataDisk.ps1{0}', parameters('_artifactsLocationSasToken')))]", + "[uri(parameters('_artifactsLocation'), format('scripts/MountFileShare.ps1{0}', parameters('_artifactsLocationSasToken')))]", + "[uri(parameters('_artifactsLocation'), format('scripts/p4DepotSync.ps1{0}', parameters('_artifactsLocationSasToken')))]", + "[uri(parameters('_artifactsLocation'), format('scripts/ibSetup.ps1{0}', parameters('_artifactsLocationSasToken')))]", + "[uri(parameters('_artifactsLocation'), format('scripts/PostInstall.ps1{0}', parameters('_artifactsLocationSasToken')))]", + "[uri(parameters('_artifactsLocation'), format('scripts/PreInstall.zip{0}', parameters('_artifactsLocationSasToken')))]" ], "commandToExecute": "[format('powershell -ExecutionPolicy Unrestricted -NoProfile -NonInteractive -command \"./CreateDataDisk.ps1;./MountFileShare.ps1 {0};./p4DepotSync.ps1 {1};./ibSetup.ps1;{2};./PostInstall.ps1\"', variables('mountFileShareParams'), variables('p4Params'), parameters('cmdGDKInstall'))]" } diff --git a/application-workloads/azure-gamedev/gamedev-vm/azuredeploy.parameters.json b/application-workloads/azure-gamedev/gamedev-vm/azuredeploy.parameters.json index 37d742950974..7714ab04875b 100644 --- a/application-workloads/azure-gamedev/gamedev-vm/azuredeploy.parameters.json +++ b/application-workloads/azure-gamedev/gamedev-vm/azuredeploy.parameters.json @@ -2,11 +2,11 @@ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", "contentVersion": "1.0.0.0", "parameters": { - "adminName": { - "value": "dcibad" + "administratorLogin": { + "value": "GEN-UNIQUE" }, - "adminPass": { - "value": "B1g-D6taRocks" + "passwordAdministratorLogin": { + "value": "GEN-UNIQUE" }, "vmSize": { "value": "Standard_NV12s_v3" diff --git a/application-workloads/azure-gamedev/gamedev-vm/gamedev-ama.bicep b/application-workloads/azure-gamedev/gamedev-vm/gamedev-ama.bicep deleted file mode 100644 index d7f0d656349c..000000000000 --- a/application-workloads/azure-gamedev/gamedev-vm/gamedev-ama.bicep +++ /dev/null @@ -1,79 +0,0 @@ - -param location string = resourceGroup().location - -@allowed([ - 'ue_4_27' - 'ue_5_0ea' -]) -param gameEngine string = 'ue_4_27' -@allowed([ - 'win10' - 'ws2019' -]) -param osType string = 'win10' - -@allowed([ - 'Standard_NC4as_T4_v3' - 'Standard_NC8as_T4_v3' - 'Standard_NC16as_T4_v3' - 'Standard_NC64as_T4_v3' - 'Standard_NV6' - 'Standard_NV12' - 'Standard_NV24' - 'Standard_NV12s_v3' - 'Standard_NV24s_v3' - 'Standard_NV48s_v3' -]) -param vmSize string = 'Standard_NV12s_v3' - -param administratorLogin string -@secure() -param passwordAdministratorLogin string - -@description('Remote Access technology') -@allowed([ - 'RDP' - 'Teradici' - 'Parsec' -]) -param remoteAccessTechnology string = 'RDP' - -param managedResourceGroupId string = '' - -resource solution_resource 'Microsoft.Solutions/applications@2017-09-01' = { - name : 'gamingDevVM' - location: location - kind : 'MarketPlace' - plan : { - 'name': 'game-dev-vm-amatest-plan' - 'product': 'f39e77ad-ada5-4145-9291-643e6e3ce1b2' - 'publisher': 'microsoftcorporation1602274591143' - 'version': '0.1.121' - } - properties: { - managedResourceGroupId: (empty(managedResourceGroupId) ? '${subscription().id}/resourceGroups/${take('${resourceGroup().name}-mrg', 90)}' : managedResourceGroupId) - parameters: { - location: { - value: location - } - vmSize: { - value: vmSize - } - adminName: { - value: administratorLogin - } - adminPass: { - value: passwordAdministratorLogin - } - osType: { - value: osType - } - gameEngine: { - value: gameEngine - } - remoteAccessTechnology: { - value: remoteAccessTechnology - } - } - } -} diff --git a/application-workloads/azure-gamedev/gamedev-vm/main.bicep b/application-workloads/azure-gamedev/gamedev-vm/main.bicep index 4f7fe0f1c25d..83e68b27e65f 100644 --- a/application-workloads/azure-gamedev/gamedev-vm/main.bicep +++ b/application-workloads/azure-gamedev/gamedev-vm/main.bicep @@ -38,7 +38,7 @@ param passwordAdministratorLogin string ]) param remoteAccessTechnology string = 'RDP' -module gamedevvm 'gamedev-vm.bicep' = { +module gamedevvm './nestedtemplates/gamedev-vm.bicep' = { name : 'gamingDevVM' params: { location: location diff --git a/application-workloads/azure-gamedev/gamedev-vm/gamedev-vm.bicep b/application-workloads/azure-gamedev/gamedev-vm/nestedtemplates/gamedev-vm.bicep similarity index 98% rename from application-workloads/azure-gamedev/gamedev-vm/gamedev-vm.bicep rename to application-workloads/azure-gamedev/gamedev-vm/nestedtemplates/gamedev-vm.bicep index 7923fb578eff..0aae185ec907 100644 --- a/application-workloads/azure-gamedev/gamedev-vm/gamedev-vm.bicep +++ b/application-workloads/azure-gamedev/gamedev-vm/nestedtemplates/gamedev-vm.bicep @@ -150,7 +150,7 @@ param environment string = 'production' param outTagsByResource object = {} @description('The base URI where artifacts required by this template are located including a trailing \'/\'') -param _artifactsLocation string = 'https://raw.githubusercontent.com/dciborow/azure-quickstart-templates/dciborow/gamedev-vm/application-workloads/azure-gamedev/gamedev-vm/' +param _artifactsLocation string = '' @description('The sasToken required to access _artifactsLocation.') @secure() diff --git a/application-workloads/azure-gamedev/gamedev-vm/nsgRules.bicep b/application-workloads/azure-gamedev/gamedev-vm/nestedtemplates/nsgRules.bicep similarity index 100% rename from application-workloads/azure-gamedev/gamedev-vm/nsgRules.bicep rename to application-workloads/azure-gamedev/gamedev-vm/nestedtemplates/nsgRules.bicep diff --git a/application-workloads/azure-gamedev/gamedev-vm/remoteAccessExtension.bicep b/application-workloads/azure-gamedev/gamedev-vm/nestedtemplates/remoteAccessExtension.bicep similarity index 68% rename from application-workloads/azure-gamedev/gamedev-vm/remoteAccessExtension.bicep rename to application-workloads/azure-gamedev/gamedev-vm/nestedtemplates/remoteAccessExtension.bicep index 75ab80ddabf4..eb1cee678acf 100644 --- a/application-workloads/azure-gamedev/gamedev-vm/remoteAccessExtension.bicep +++ b/application-workloads/azure-gamedev/gamedev-vm/nestedtemplates/remoteAccessExtension.bicep @@ -26,10 +26,10 @@ var remoteAccessExtension = { autoUpgradeMinorVersion: true settings: { fileUris: [ - uri(_artifactsLocation, 'CreateDataDisk.ps1${_artifactsLocationSasToken}') - uri(_artifactsLocation, 'MountFileShare.ps1${_artifactsLocationSasToken}') - uri(_artifactsLocation, 'p4DepotSync.ps1${_artifactsLocationSasToken}') - uri(_artifactsLocation, 'ibSetup.ps1${_artifactsLocationSasToken}') + uri(_artifactsLocation, 'scripts/CreateDataDisk.ps1${_artifactsLocationSasToken}') + uri(_artifactsLocation, 'scripts/MountFileShare.ps1${_artifactsLocationSasToken}') + uri(_artifactsLocation, 'scripts/p4DepotSync.ps1${_artifactsLocationSasToken}') + uri(_artifactsLocation, 'scripts/ibSetup.ps1${_artifactsLocationSasToken}') ] commandToExecute: 'powershell -ExecutionPolicy Unrestricted -NoProfile -NonInteractive -command "./CreateDataDisk.ps1;./MountFileShare.ps1 ${mountFileShareParams};./p4DepotSync.ps1 ${p4Params};./ibSetup.ps1;${cmdGDKInstall}"' } @@ -41,11 +41,11 @@ var remoteAccessExtension = { autoUpgradeMinorVersion: true settings: { fileUris: [ - uri(_artifactsLocation, 'CreateDataDisk.ps1${_artifactsLocationSasToken}') - uri(_artifactsLocation, 'MountFileShare.ps1${_artifactsLocationSasToken}') - uri(_artifactsLocation, 'p4DepotSync.ps1${_artifactsLocationSasToken}') - uri(_artifactsLocation, 'ibSetup.ps1${_artifactsLocationSasToken}') - uri(_artifactsLocation, 'TeradiciRegCAS.ps1${_artifactsLocationSasToken}') + uri(_artifactsLocation, 'scripts/CreateDataDisk.ps1${_artifactsLocationSasToken}') + uri(_artifactsLocation, 'scripts/MountFileShare.ps1${_artifactsLocationSasToken}') + uri(_artifactsLocation, 'scripts/p4DepotSync.ps1${_artifactsLocationSasToken}') + uri(_artifactsLocation, 'scripts/ibSetup.ps1${_artifactsLocationSasToken}') + uri(_artifactsLocation, 'scripts/TeradiciRegCAS.ps1${_artifactsLocationSasToken}') ] } protectedSettings: { @@ -59,12 +59,12 @@ var remoteAccessExtension = { autoUpgradeMinorVersion: true settings: { fileUris: [ - uri(_artifactsLocation, 'CreateDataDisk.ps1${_artifactsLocationSasToken}') - uri(_artifactsLocation, 'MountFileShare.ps1${_artifactsLocationSasToken}') - uri(_artifactsLocation, 'p4DepotSync.ps1${_artifactsLocationSasToken}') - uri(_artifactsLocation, 'ibSetup.ps1${_artifactsLocationSasToken}') - uri(_artifactsLocation, 'PostInstall.ps1${_artifactsLocationSasToken}') - uri(_artifactsLocation, 'PreInstall.zip${_artifactsLocationSasToken}') + uri(_artifactsLocation, 'scripts/CreateDataDisk.ps1${_artifactsLocationSasToken}') + uri(_artifactsLocation, 'scripts/MountFileShare.ps1${_artifactsLocationSasToken}') + uri(_artifactsLocation, 'scripts/p4DepotSync.ps1${_artifactsLocationSasToken}') + uri(_artifactsLocation, 'scripts/ibSetup.ps1${_artifactsLocationSasToken}') + uri(_artifactsLocation, 'scripts/PostInstall.ps1${_artifactsLocationSasToken}') + uri(_artifactsLocation, 'scripts/PreInstall.zip${_artifactsLocationSasToken}') ] commandToExecute: 'powershell -ExecutionPolicy Unrestricted -NoProfile -NonInteractive -command "./CreateDataDisk.ps1;./MountFileShare.ps1 ${mountFileShareParams};./p4DepotSync.ps1 ${p4Params};./ibSetup.ps1;${cmdGDKInstall};./PostInstall.ps1"' } diff --git a/application-workloads/azure-gamedev/gamedev-vm/MountFIleShare.ps1 b/application-workloads/azure-gamedev/gamedev-vm/scripts/MountFIleShare.ps1 similarity index 100% rename from application-workloads/azure-gamedev/gamedev-vm/MountFIleShare.ps1 rename to application-workloads/azure-gamedev/gamedev-vm/scripts/MountFIleShare.ps1 diff --git a/application-workloads/azure-gamedev/gamedev-vm/TeradiciRegCAS.ps1 b/application-workloads/azure-gamedev/gamedev-vm/scripts/TeradiciRegCAS.ps1 similarity index 100% rename from application-workloads/azure-gamedev/gamedev-vm/TeradiciRegCAS.ps1 rename to application-workloads/azure-gamedev/gamedev-vm/scripts/TeradiciRegCAS.ps1 diff --git a/application-workloads/azure-gamedev/gamedev-vm/createDataDisk.ps1 b/application-workloads/azure-gamedev/gamedev-vm/scripts/createDataDisk.ps1 similarity index 100% rename from application-workloads/azure-gamedev/gamedev-vm/createDataDisk.ps1 rename to application-workloads/azure-gamedev/gamedev-vm/scripts/createDataDisk.ps1 diff --git a/application-workloads/azure-gamedev/gamedev-vm/ibSetup.ps1 b/application-workloads/azure-gamedev/gamedev-vm/scripts/ibSetup.ps1 similarity index 100% rename from application-workloads/azure-gamedev/gamedev-vm/ibSetup.ps1 rename to application-workloads/azure-gamedev/gamedev-vm/scripts/ibSetup.ps1 diff --git a/application-workloads/azure-gamedev/gamedev-vm/p4DepotSync.ps1 b/application-workloads/azure-gamedev/gamedev-vm/scripts/p4DepotSync.ps1 similarity index 100% rename from application-workloads/azure-gamedev/gamedev-vm/p4DepotSync.ps1 rename to application-workloads/azure-gamedev/gamedev-vm/scripts/p4DepotSync.ps1 From 553c38885e31c1f4b38703f48e096967c4426b01 Mon Sep 17 00:00:00 2001 From: Daniel Ciborowski Date: Tue, 12 Apr 2022 19:24:06 -0400 Subject: [PATCH 20/71] fix api version and params --- .../azure-gamedev/gamedev-vm/azuredeploy.json | 22 +++++++++---------- .../azure-gamedev/gamedev-vm/main.bicep | 9 ++++++++ .../nestedtemplates/gamedev-vm.bicep | 14 ++++++------ .../remoteAccessExtension.bicep | 2 +- 4 files changed, 28 insertions(+), 19 deletions(-) diff --git a/application-workloads/azure-gamedev/gamedev-vm/azuredeploy.json b/application-workloads/azure-gamedev/gamedev-vm/azuredeploy.json index 671665984afa..3a27c2069f99 100644 --- a/application-workloads/azure-gamedev/gamedev-vm/azuredeploy.json +++ b/application-workloads/azure-gamedev/gamedev-vm/azuredeploy.json @@ -5,7 +5,7 @@ "_generator": { "name": "bicep", "version": "0.5.6.12127", - "templateHash": "14032160832736697226" + "templateHash": "16853573859906785262" } }, "parameters": { @@ -104,7 +104,7 @@ "_generator": { "name": "bicep", "version": "0.5.6.12127", - "templateHash": "4432448743094339894" + "templateHash": "14157751438790181045" } }, "parameters": { @@ -503,7 +503,7 @@ "resources": [ { "type": "Microsoft.Resources/deployments", - "apiVersion": "2020-06-01", + "apiVersion": "2021-04-01", "name": "pid-7837dd60-4ba8-419a-a26f-237bbe170773-partnercenter", "properties": { "mode": "Incremental", @@ -517,7 +517,7 @@ { "condition": "[equals(parameters('publicIpNewOrExisting'), 'new')]", "type": "Microsoft.Network/publicIPAddresses", - "apiVersion": "2021-03-01", + "apiVersion": "2021-05-01", "name": "[parameters('publicIpName')]", "sku": { "name": "[parameters('publicIpSku')]" @@ -532,7 +532,7 @@ }, { "type": "Microsoft.Network/networkSecurityGroups", - "apiVersion": "2021-03-01", + "apiVersion": "2021-05-01", "name": "[variables('nsgName_var')]", "location": "[parameters('location')]", "properties": { @@ -545,7 +545,7 @@ { "condition": "[equals(parameters('vnetNewOrExisting'), 'new')]", "type": "Microsoft.Network/virtualNetworks", - "apiVersion": "2021-03-01", + "apiVersion": "2021-05-01", "name": "[parameters('vnetName')]", "location": "[parameters('location')]", "properties": { @@ -566,7 +566,7 @@ }, { "type": "Microsoft.Network/networkInterfaces", - "apiVersion": "2021-03-01", + "apiVersion": "2021-05-01", "name": "[variables('nicName_var')]", "location": "[parameters('location')]", "properties": { @@ -588,7 +588,7 @@ }, { "type": "Microsoft.Compute/virtualMachines", - "apiVersion": "2021-03-01", + "apiVersion": "2021-11-01", "name": "[variables('vmName_var')]", "location": "[parameters('location')]", "plan": "[variables('vmPlan')]", @@ -642,7 +642,7 @@ { "condition": "[parameters('enableAAD')]", "type": "Microsoft.Compute/virtualMachines/extensions", - "apiVersion": "2019-12-01", + "apiVersion": "2021-11-01", "name": "[format('{0}/AADLoginForWindows', variables('vmName_var'))]", "location": "[parameters('location')]", "properties": { @@ -769,7 +769,7 @@ "_generator": { "name": "bicep", "version": "0.5.6.12127", - "templateHash": "1649725417760946162" + "templateHash": "18043769457075048777" } }, "parameters": { @@ -881,7 +881,7 @@ "resources": [ { "type": "Microsoft.Compute/virtualMachines/extensions", - "apiVersion": "2019-12-01", + "apiVersion": "2021-11-01", "name": "[format('{0}/CustomScriptExtension-{1}', parameters('virtualMachineName'), parameters('remoteAccessTechnology'))]", "location": "[parameters('location')]", "properties": "[variables('remoteAccessExtension')[parameters('remoteAccessTechnology')]]" diff --git a/application-workloads/azure-gamedev/gamedev-vm/main.bicep b/application-workloads/azure-gamedev/gamedev-vm/main.bicep index 83e68b27e65f..d52e64c20644 100644 --- a/application-workloads/azure-gamedev/gamedev-vm/main.bicep +++ b/application-workloads/azure-gamedev/gamedev-vm/main.bicep @@ -1,6 +1,13 @@ param location string = resourceGroup().location +@description('The base URI where artifacts required by this template are located including a trailing \'/\'') +param _artifactsLocation string = '' + +@description('The sasToken required to access _artifactsLocation.') +@secure() +param _artifactsLocationSasToken string = '' + @allowed([ 'ue_4_27' 'ue_5_0ea' @@ -48,5 +55,7 @@ module gamedevvm './nestedtemplates/gamedev-vm.bicep' = { osType: osType gameEngine: gameEngine remoteAccessTechnology: remoteAccessTechnology + _artifactsLocation: _artifactsLocation + _artifactsLocationSasToken: _artifactsLocationSasToken } } diff --git a/application-workloads/azure-gamedev/gamedev-vm/nestedtemplates/gamedev-vm.bicep b/application-workloads/azure-gamedev/gamedev-vm/nestedtemplates/gamedev-vm.bicep index 0aae185ec907..276b431dec6b 100644 --- a/application-workloads/azure-gamedev/gamedev-vm/nestedtemplates/gamedev-vm.bicep +++ b/application-workloads/azure-gamedev/gamedev-vm/nestedtemplates/gamedev-vm.bicep @@ -232,7 +232,7 @@ var publicIpId = { 'none': '' }[publicIpNewOrExisting] -resource partnercenter 'Microsoft.Resources/deployments@2020-06-01' = { +resource partnercenter 'Microsoft.Resources/deployments@2021-04-01' = { name: 'pid-7837dd60-4ba8-419a-a26f-237bbe170773-partnercenter' properties: { mode: 'Incremental' @@ -244,7 +244,7 @@ resource partnercenter 'Microsoft.Resources/deployments@2020-06-01' = { } } -resource publicIp 'Microsoft.Network/publicIPAddresses@2021-03-01' = if (publicIpNewOrExisting == 'new') { +resource publicIp 'Microsoft.Network/publicIPAddresses@2021-05-01' = if (publicIpNewOrExisting == 'new') { name: publicIpName sku: { name: publicIpSku @@ -265,7 +265,7 @@ module nsg_rules 'nsgRules.bicep' = { } } -resource nsg 'Microsoft.Network/networkSecurityGroups@2021-03-01' = { +resource nsg 'Microsoft.Network/networkSecurityGroups@2021-05-01' = { name: nsgName_var location: location properties: { @@ -273,7 +273,7 @@ resource nsg 'Microsoft.Network/networkSecurityGroups@2021-03-01' = { } } -resource vnet 'Microsoft.Network/virtualNetworks@2021-03-01' = if (vnetNewOrExisting == 'new') { +resource vnet 'Microsoft.Network/virtualNetworks@2021-05-01' = if (vnetNewOrExisting == 'new') { name: vnetName location: location properties: { @@ -293,7 +293,7 @@ resource vnet 'Microsoft.Network/virtualNetworks@2021-03-01' = if (vnetNewOrExis } } -resource nic 'Microsoft.Network/networkInterfaces@2021-03-01' = { +resource nic 'Microsoft.Network/networkInterfaces@2021-05-01' = { name: nicName_var location: location dependsOn: [ @@ -323,7 +323,7 @@ resource nic 'Microsoft.Network/networkInterfaces@2021-03-01' = { } } -resource virtualMachine 'Microsoft.Compute/virtualMachines@2021-03-01' = { +resource virtualMachine 'Microsoft.Compute/virtualMachines@2021-11-01' = { name: vmName_var location: location plan: vmPlan @@ -390,7 +390,7 @@ module remoteAccess 'remoteAccessExtension.bicep' = { } } -resource virtualMachine_enableAAD 'Microsoft.Compute/virtualMachines/extensions@2019-12-01' = if(enableAAD) { +resource virtualMachine_enableAAD 'Microsoft.Compute/virtualMachines/extensions@2021-11-01' = if(enableAAD) { name : '${virtualMachine.name}/AADLoginForWindows' location : location dependsOn : [ diff --git a/application-workloads/azure-gamedev/gamedev-vm/nestedtemplates/remoteAccessExtension.bicep b/application-workloads/azure-gamedev/gamedev-vm/nestedtemplates/remoteAccessExtension.bicep index eb1cee678acf..084ccaf6d9b5 100644 --- a/application-workloads/azure-gamedev/gamedev-vm/nestedtemplates/remoteAccessExtension.bicep +++ b/application-workloads/azure-gamedev/gamedev-vm/nestedtemplates/remoteAccessExtension.bicep @@ -71,7 +71,7 @@ var remoteAccessExtension = { } } -resource virtualMachine_remoteAccessExtension 'Microsoft.Compute/virtualMachines/extensions@2019-12-01' = { +resource virtualMachine_remoteAccessExtension 'Microsoft.Compute/virtualMachines/extensions@2021-11-01' = { name : '${virtualMachineName}/CustomScriptExtension-${remoteAccessTechnology}' location : location properties: remoteAccessExtension[remoteAccessTechnology] From a604027483f8f7395d6f85db7e13004b899f876a Mon Sep 17 00:00:00 2001 From: Daniel Ciborowski Date: Tue, 12 Apr 2022 19:29:32 -0400 Subject: [PATCH 21/71] update readme --- .../azure-gamedev/gamedev-vm/README.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/application-workloads/azure-gamedev/gamedev-vm/README.md b/application-workloads/azure-gamedev/gamedev-vm/README.md index 8b137891791f..16816c9d1d8a 100644 --- a/application-workloads/azure-gamedev/gamedev-vm/README.md +++ b/application-workloads/azure-gamedev/gamedev-vm/README.md @@ -1 +1,15 @@ +# Setup Azure Game Development Virtual Machine + +![Azure Public Test Date](https://azurequickstartsservice.blob.core.windows.net/badges/application-workloads/azure-gamedev/gamdev-vm/PublicLastTestDate.svg) +![Azure Public Test Result](https://azurequickstartsservice.blob.core.windows.net/badges/application-workloads/azure-gamedev/gamdev-vm/PublicDeployment.svg) + +![Azure US Gov Last Test Date](https://azurequickstartsservice.blob.core.windows.net/badges/application-workloads/azure-gamedev/gamdev-vm/FairfaxLastTestDate.svg) +![Azure US Gov Last Test Result](https://azurequickstartsservice.blob.core.windows.net/badges/application-workloads/azure-gamedev/gamdev-vm/FairfaxDeployment.svg) + +![Best Practice Check](https://azurequickstartsservice.blob.core.windows.net/badges/application-workloads/azure-gamedev/gamdev-vm/BestPracticeResult.svg) +![Cred Scan Check](https://azurequickstartsservice.blob.core.windows.net/badges/application-workloads/azure-gamedev/gamdev-vm/CredScanResult.svg) + +[![Deploy To Azure](https://raw.githubusercontent.com/Azure/azure-quickstart-templates/master/1-CONTRIBUTION-GUIDE/images/deploytoazure.svg?sanitize=true)](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2FAzure%2Fazure-quickstart-templates%2Fmaster%2Fapplication-workloads%2Fbosh%2Fbosh-setup%2Fazuredeploy.json) [![Deploy To Azure US Gov](https://raw.githubusercontent.com/Azure/azure-quickstart-templates/master/1-CONTRIBUTION-GUIDE/images/deploytoazuregov.svg?sanitize=true)](https://portal.azure.us/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2FAzure%2Fazure-quickstart-templates%2Fmaster%2Fapplication-workloads%2Fbosh%2Fbosh-setup%2Fazuredeploy.json) [![Visualize](https://raw.githubusercontent.com/Azure/azure-quickstart-templates/master/1-CONTRIBUTION-GUIDE/images/visualizebutton.svg?sanitize=true)](http://armviz.io/#/?load=https%3A%2F%2Fraw.githubusercontent.com%2FAzure%2Fazure-quickstart-templates%2Fmaster%2Fapplication-workloads%2Fbosh%2Fbosh-setup%2Fazuredeploy.json) + +This template can help you setup the development environment to deploy [Azure Game Development Virutal Machine](https://docs.microsoft.com/en-us/gaming/azure/game-dev-virtual-machine/) on Azure Cloud, Azure China Cloud, Azure Germany Cloud, Azure USGovernment Cloud and Azure Stack. From b72e4271f78ca0236c25745626ec4245c8a3aa95 Mon Sep 17 00:00:00 2001 From: Daniel Ciborowski Date: Tue, 12 Apr 2022 19:37:45 -0400 Subject: [PATCH 22/71] update arm --- .../azure-gamedev/gamedev-vm/azuredeploy.json | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/application-workloads/azure-gamedev/gamedev-vm/azuredeploy.json b/application-workloads/azure-gamedev/gamedev-vm/azuredeploy.json index 3a27c2069f99..a5d021aaa727 100644 --- a/application-workloads/azure-gamedev/gamedev-vm/azuredeploy.json +++ b/application-workloads/azure-gamedev/gamedev-vm/azuredeploy.json @@ -5,7 +5,7 @@ "_generator": { "name": "bicep", "version": "0.5.6.12127", - "templateHash": "16853573859906785262" + "templateHash": "8620095526082356698" } }, "parameters": { @@ -13,6 +13,20 @@ "type": "string", "defaultValue": "[resourceGroup().location]" }, + "_artifactsLocation": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The base URI where artifacts required by this template are located including a trailing '/'" + } + }, + "_artifactsLocationSasToken": { + "type": "secureString", + "defaultValue": "", + "metadata": { + "description": "The sasToken required to access _artifactsLocation." + } + }, "gameEngine": { "type": "string", "defaultValue": "ue_4_27", @@ -95,6 +109,12 @@ }, "remoteAccessTechnology": { "value": "[parameters('remoteAccessTechnology')]" + }, + "_artifactsLocation": { + "value": "[parameters('_artifactsLocation')]" + }, + "_artifactsLocationSasToken": { + "value": "[parameters('_artifactsLocationSasToken')]" } }, "template": { From c164310e8cee56cd3d205e14e765b831891096c8 Mon Sep 17 00:00:00 2001 From: Daniel Ciborowski Date: Thu, 21 Apr 2022 09:08:38 -0400 Subject: [PATCH 23/71] Update README.md --- application-workloads/azure-gamedev/gamedev-vm/README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/application-workloads/azure-gamedev/gamedev-vm/README.md b/application-workloads/azure-gamedev/gamedev-vm/README.md index 16816c9d1d8a..c8f68b29e374 100644 --- a/application-workloads/azure-gamedev/gamedev-vm/README.md +++ b/application-workloads/azure-gamedev/gamedev-vm/README.md @@ -1,4 +1,3 @@ - # Setup Azure Game Development Virtual Machine ![Azure Public Test Date](https://azurequickstartsservice.blob.core.windows.net/badges/application-workloads/azure-gamedev/gamdev-vm/PublicLastTestDate.svg) From b0f0676cbeda7167fabf3ae118a3067bb5b30e15 Mon Sep 17 00:00:00 2001 From: Daniel Ciborowski Date: Thu, 21 Apr 2022 09:15:03 -0400 Subject: [PATCH 24/71] Update README.md --- application-workloads/azure-gamedev/gamedev-vm/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application-workloads/azure-gamedev/gamedev-vm/README.md b/application-workloads/azure-gamedev/gamedev-vm/README.md index c8f68b29e374..55ec700555f1 100644 --- a/application-workloads/azure-gamedev/gamedev-vm/README.md +++ b/application-workloads/azure-gamedev/gamedev-vm/README.md @@ -1,6 +1,6 @@ # Setup Azure Game Development Virtual Machine -![Azure Public Test Date](https://azurequickstartsservice.blob.core.windows.net/badges/application-workloads/azure-gamedev/gamdev-vm/PublicLastTestDate.svg) +![Azure Public Test Date](https://azurequickstartsservice.blob.core.windows.net/badges/application-workloads/azure-gamedev/gamedev-vm/PublicLastTestDate.svg) ![Azure Public Test Result](https://azurequickstartsservice.blob.core.windows.net/badges/application-workloads/azure-gamedev/gamdev-vm/PublicDeployment.svg) ![Azure US Gov Last Test Date](https://azurequickstartsservice.blob.core.windows.net/badges/application-workloads/azure-gamedev/gamdev-vm/FairfaxLastTestDate.svg) From 7887e00ac3d797d9a043fa9f96969427bc9b35a5 Mon Sep 17 00:00:00 2001 From: Daniel Ciborowski Date: Thu, 21 Apr 2022 09:16:03 -0400 Subject: [PATCH 25/71] Update README.md --- .../azure-gamedev/gamedev-vm/README.md | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/application-workloads/azure-gamedev/gamedev-vm/README.md b/application-workloads/azure-gamedev/gamedev-vm/README.md index 55ec700555f1..59558b4fc0cb 100644 --- a/application-workloads/azure-gamedev/gamedev-vm/README.md +++ b/application-workloads/azure-gamedev/gamedev-vm/README.md @@ -1,14 +1,15 @@ # Setup Azure Game Development Virtual Machine - ![Azure Public Test Date](https://azurequickstartsservice.blob.core.windows.net/badges/application-workloads/azure-gamedev/gamedev-vm/PublicLastTestDate.svg) -![Azure Public Test Result](https://azurequickstartsservice.blob.core.windows.net/badges/application-workloads/azure-gamedev/gamdev-vm/PublicDeployment.svg) +![Azure Public Test Result](https://azurequickstartsservice.blob.core.windows.net/badges/application-workloads/azure-gamedev/gamedev-vm/PublicDeployment.svg) -![Azure US Gov Last Test Date](https://azurequickstartsservice.blob.core.windows.net/badges/application-workloads/azure-gamedev/gamdev-vm/FairfaxLastTestDate.svg) -![Azure US Gov Last Test Result](https://azurequickstartsservice.blob.core.windows.net/badges/application-workloads/azure-gamedev/gamdev-vm/FairfaxDeployment.svg) +![Azure US Gov Last Test Date](https://azurequickstartsservice.blob.core.windows.net/badges/application-workloads/azure-gamedev/gamedev-vm/FairfaxLastTestDate.svg) +![Azure US Gov Last Test Result](https://azurequickstartsservice.blob.core.windows.net/badges/application-workloads/azure-gamedev/gamedev-vm/FairfaxDeployment.svg) -![Best Practice Check](https://azurequickstartsservice.blob.core.windows.net/badges/application-workloads/azure-gamedev/gamdev-vm/BestPracticeResult.svg) -![Cred Scan Check](https://azurequickstartsservice.blob.core.windows.net/badges/application-workloads/azure-gamedev/gamdev-vm/CredScanResult.svg) +![Best Practice Check](https://azurequickstartsservice.blob.core.windows.net/badges/application-workloads/azure-gamedev/gamedev-vm/BestPracticeResult.svg) +![Cred Scan Check](https://azurequickstartsservice.blob.core.windows.net/badges/application-workloads/azure-gamedev/gamedev-vm/CredScanResult.svg) -[![Deploy To Azure](https://raw.githubusercontent.com/Azure/azure-quickstart-templates/master/1-CONTRIBUTION-GUIDE/images/deploytoazure.svg?sanitize=true)](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2FAzure%2Fazure-quickstart-templates%2Fmaster%2Fapplication-workloads%2Fbosh%2Fbosh-setup%2Fazuredeploy.json) [![Deploy To Azure US Gov](https://raw.githubusercontent.com/Azure/azure-quickstart-templates/master/1-CONTRIBUTION-GUIDE/images/deploytoazuregov.svg?sanitize=true)](https://portal.azure.us/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2FAzure%2Fazure-quickstart-templates%2Fmaster%2Fapplication-workloads%2Fbosh%2Fbosh-setup%2Fazuredeploy.json) [![Visualize](https://raw.githubusercontent.com/Azure/azure-quickstart-templates/master/1-CONTRIBUTION-GUIDE/images/visualizebutton.svg?sanitize=true)](http://armviz.io/#/?load=https%3A%2F%2Fraw.githubusercontent.com%2FAzure%2Fazure-quickstart-templates%2Fmaster%2Fapplication-workloads%2Fbosh%2Fbosh-setup%2Fazuredeploy.json) +![Bicep Version](https://azurequickstartsservice.blob.core.windows.net/badges/application-workloads/azure-gamedev/gamedev-vm/BicepVersion.svg) -This template can help you setup the development environment to deploy [Azure Game Development Virutal Machine](https://docs.microsoft.com/en-us/gaming/azure/game-dev-virtual-machine/) on Azure Cloud, Azure China Cloud, Azure Germany Cloud, Azure USGovernment Cloud and Azure Stack. +[![Deploy To Azure](https://raw.githubusercontent.com/Azure/azure-quickstart-templates/master/1-CONTRIBUTION-GUIDE/images/deploytoazure.svg?sanitize=true)](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2FAzure%2Fazure-quickstart-templates%2Fmaster%2Fapplication-workloads%2Fazure-gamedev%2Fgamedev-vm%2Fazuredeploy.json) +[![Deploy To Azure US Gov](https://raw.githubusercontent.com/Azure/azure-quickstart-templates/master/1-CONTRIBUTION-GUIDE/images/deploytoazuregov.svg?sanitize=true)](https://portal.azure.us/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2FAzure%2Fazure-quickstart-templates%2Fmaster%2Fapplication-workloads%2Fazure-gamedev%2Fgamedev-vm%2Fazuredeploy.json) +[![Visualize](https://raw.githubusercontent.com/Azure/azure-quickstart-templates/master/1-CONTRIBUTION-GUIDE/images/visualizebutton.svg?sanitize=true)](http://armviz.io/#/?load=https%3A%2F%2Fraw.githubusercontent.com%2FAzure%2Fazure-quickstart-templates%2Fmaster%2Fapplication-workloads%2Fazure-gamedev%2Fgamedev-vm%2Fazuredeploy.json) From b142a03becbe6ce061480768ed8de1fea437a12a Mon Sep 17 00:00:00 2001 From: Daniel Ciborowski Date: Thu, 21 Apr 2022 10:06:07 -0400 Subject: [PATCH 26/71] fix per Build comments --- .../azure-gamedev/gamedev-vm/azuredeploy.json | 4 ++-- application-workloads/azure-gamedev/gamedev-vm/main.bicep | 2 +- .../nestedtemplates/remoteAccessExtension.bicep | 8 ++++++-- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/application-workloads/azure-gamedev/gamedev-vm/azuredeploy.json b/application-workloads/azure-gamedev/gamedev-vm/azuredeploy.json index a5d021aaa727..945ea76543b5 100644 --- a/application-workloads/azure-gamedev/gamedev-vm/azuredeploy.json +++ b/application-workloads/azure-gamedev/gamedev-vm/azuredeploy.json @@ -5,7 +5,7 @@ "_generator": { "name": "bicep", "version": "0.5.6.12127", - "templateHash": "8620095526082356698" + "templateHash": "13123207943271125566" } }, "parameters": { @@ -15,7 +15,7 @@ }, "_artifactsLocation": { "type": "string", - "defaultValue": "", + "defaultValue": "[deployment().properties.templateLink.uri]", "metadata": { "description": "The base URI where artifacts required by this template are located including a trailing '/'" } diff --git a/application-workloads/azure-gamedev/gamedev-vm/main.bicep b/application-workloads/azure-gamedev/gamedev-vm/main.bicep index d52e64c20644..5ce924258853 100644 --- a/application-workloads/azure-gamedev/gamedev-vm/main.bicep +++ b/application-workloads/azure-gamedev/gamedev-vm/main.bicep @@ -2,7 +2,7 @@ param location string = resourceGroup().location @description('The base URI where artifacts required by this template are located including a trailing \'/\'') -param _artifactsLocation string = '' +param _artifactsLocation string = deployment().properties.templateLink.uri @description('The sasToken required to access _artifactsLocation.') @secure() diff --git a/application-workloads/azure-gamedev/gamedev-vm/nestedtemplates/remoteAccessExtension.bicep b/application-workloads/azure-gamedev/gamedev-vm/nestedtemplates/remoteAccessExtension.bicep index 084ccaf6d9b5..f2ab0a5970cd 100644 --- a/application-workloads/azure-gamedev/gamedev-vm/nestedtemplates/remoteAccessExtension.bicep +++ b/application-workloads/azure-gamedev/gamedev-vm/nestedtemplates/remoteAccessExtension.bicep @@ -1,15 +1,19 @@ +param _artifactsLocation string = deployment().properties.templateLink.uri +@secure() +param _artifactsLocationSasToken string = '' + param cmdGDKInstall string param cmdTeradiciRegistration string -param _artifactsLocation string -param _artifactsLocationSasToken string param virtualMachineName string param remoteAccessTechnology string param location string param fileShareStorageAccount string +@secure() param fileShareStorageAccountKey string param fileShareName string param p4Port string param p4Username string +@secure() param p4Password string param p4Workspace string param p4Stream string From 0bf253f3cbfb1ed0191015df58ba30336fff7bcc Mon Sep 17 00:00:00 2001 From: Daniel Ciborowski Date: Thu, 21 Apr 2022 11:29:38 -0400 Subject: [PATCH 27/71] fix arm --- .../azure-gamedev/gamedev-vm/azuredeploy.json | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/application-workloads/azure-gamedev/gamedev-vm/azuredeploy.json b/application-workloads/azure-gamedev/gamedev-vm/azuredeploy.json index 945ea76543b5..0a9ad4865c1c 100644 --- a/application-workloads/azure-gamedev/gamedev-vm/azuredeploy.json +++ b/application-workloads/azure-gamedev/gamedev-vm/azuredeploy.json @@ -5,7 +5,7 @@ "_generator": { "name": "bicep", "version": "0.5.6.12127", - "templateHash": "13123207943271125566" + "templateHash": "13565078067197407014" } }, "parameters": { @@ -124,7 +124,7 @@ "_generator": { "name": "bicep", "version": "0.5.6.12127", - "templateHash": "14157751438790181045" + "templateHash": "10742678292711052642" } }, "parameters": { @@ -789,20 +789,22 @@ "_generator": { "name": "bicep", "version": "0.5.6.12127", - "templateHash": "18043769457075048777" + "templateHash": "10965725395189793781" } }, "parameters": { - "cmdGDKInstall": { - "type": "string" + "_artifactsLocation": { + "type": "string", + "defaultValue": "[deployment().properties.templateLink.uri]" }, - "cmdTeradiciRegistration": { - "type": "string" + "_artifactsLocationSasToken": { + "type": "secureString", + "defaultValue": "" }, - "_artifactsLocation": { + "cmdGDKInstall": { "type": "string" }, - "_artifactsLocationSasToken": { + "cmdTeradiciRegistration": { "type": "string" }, "virtualMachineName": { @@ -818,7 +820,7 @@ "type": "string" }, "fileShareStorageAccountKey": { - "type": "string" + "type": "secureString" }, "fileShareName": { "type": "string" @@ -830,7 +832,7 @@ "type": "string" }, "p4Password": { - "type": "string" + "type": "secureString" }, "p4Workspace": { "type": "string" From af4a68c0bf989f907c00238d030f033900bc19ff Mon Sep 17 00:00:00 2001 From: Daniel Ciborowski Date: Thu, 21 Apr 2022 13:33:38 -0400 Subject: [PATCH 28/71] set default --- .../azure-gamedev/gamedev-vm/azuredeploy.json | 6 +++--- .../gamedev-vm/nestedtemplates/gamedev-vm.bicep | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/application-workloads/azure-gamedev/gamedev-vm/azuredeploy.json b/application-workloads/azure-gamedev/gamedev-vm/azuredeploy.json index 0a9ad4865c1c..f7c823775885 100644 --- a/application-workloads/azure-gamedev/gamedev-vm/azuredeploy.json +++ b/application-workloads/azure-gamedev/gamedev-vm/azuredeploy.json @@ -5,7 +5,7 @@ "_generator": { "name": "bicep", "version": "0.5.6.12127", - "templateHash": "13565078067197407014" + "templateHash": "3569004983154617689" } }, "parameters": { @@ -124,7 +124,7 @@ "_generator": { "name": "bicep", "version": "0.5.6.12127", - "templateHash": "10742678292711052642" + "templateHash": "17557608924948392771" } }, "parameters": { @@ -433,7 +433,7 @@ }, "_artifactsLocation": { "type": "string", - "defaultValue": "", + "defaultValue": "[deployment().properties.templateLink.uri]", "metadata": { "description": "The base URI where artifacts required by this template are located including a trailing '/'" } diff --git a/application-workloads/azure-gamedev/gamedev-vm/nestedtemplates/gamedev-vm.bicep b/application-workloads/azure-gamedev/gamedev-vm/nestedtemplates/gamedev-vm.bicep index 276b431dec6b..c4936937f979 100644 --- a/application-workloads/azure-gamedev/gamedev-vm/nestedtemplates/gamedev-vm.bicep +++ b/application-workloads/azure-gamedev/gamedev-vm/nestedtemplates/gamedev-vm.bicep @@ -150,7 +150,7 @@ param environment string = 'production' param outTagsByResource object = {} @description('The base URI where artifacts required by this template are located including a trailing \'/\'') -param _artifactsLocation string = '' +param _artifactsLocation string = deployment().properties.templateLink.uri @description('The sasToken required to access _artifactsLocation.') @secure() From e820bd03b8aa23f7d890c767aa10f971d62b2870 Mon Sep 17 00:00:00 2001 From: Daniel Ciborowski Date: Thu, 21 Apr 2022 14:54:39 -0400 Subject: [PATCH 29/71] remove env options --- .../azure-gamedev/gamedev-vm/azuredeploy.json | 56 ++++--------------- .../nestedtemplates/gamedev-vm.bicep | 50 +++-------------- 2 files changed, 21 insertions(+), 85 deletions(-) diff --git a/application-workloads/azure-gamedev/gamedev-vm/azuredeploy.json b/application-workloads/azure-gamedev/gamedev-vm/azuredeploy.json index f7c823775885..fbaee244aafe 100644 --- a/application-workloads/azure-gamedev/gamedev-vm/azuredeploy.json +++ b/application-workloads/azure-gamedev/gamedev-vm/azuredeploy.json @@ -5,7 +5,7 @@ "_generator": { "name": "bicep", "version": "0.5.6.12127", - "templateHash": "3569004983154617689" + "templateHash": "748984281901363245" } }, "parameters": { @@ -124,7 +124,7 @@ "_generator": { "name": "bicep", "version": "0.5.6.12127", - "templateHash": "17557608924948392771" + "templateHash": "12443143369827810422" } }, "parameters": { @@ -416,14 +416,6 @@ "description": "Resource Group of the Public IP Address" } }, - "environment": { - "type": "string", - "defaultValue": "production", - "allowedValues": [ - "development", - "production" - ] - }, "outTagsByResource": { "type": "object", "defaultValue": {}, @@ -462,41 +454,17 @@ } }, "variables": { - "environmentMapping": { - "ue_4_27": "unreal_4_27", - "ue_5_0ea": "unreal_5_0ea", - "unity_2020_3_19f1": "unity_2020_3_19f1" - }, - "environments": { - "development": { - "vmImage": { - "publisher": "microsoft-agci-gaming", - "offer": "agci-gamedev-image", - "sku": "[format('gamedev-{0}-{1}', parameters('gameEngine'), parameters('osType'))]", - "version": "latest" - }, - "vmPlan": { - "publisher": "microsoft-agci-gaming", - "product": "agci-gamedev-image", - "name": "[format('gamedev-{0}-{1}', parameters('gameEngine'), parameters('osType'))]" - } - }, - "production": { - "vmImage": { - "publisher": "microsoftcorporation1602274591143", - "offer": "game-dev-vm", - "sku": "[format('{0}_{1}', parameters('osType'), variables('environmentMapping')[parameters('gameEngine')])]", - "version": "latest" - }, - "vmPlan": { - "publisher": "microsoftcorporation1602274591143", - "product": "game-dev-vm", - "name": "[format('{0}_{1}', parameters('osType'), variables('environmentMapping')[parameters('gameEngine')])]" - } - } + "vmImage": { + "publisher": "microsoft-agci-gaming", + "offer": "agci-gamedev-image", + "sku": "[format('gamedev-{0}-{1}', parameters('gameEngine'), parameters('osType'))]", + "version": "latest" + }, + "vmPlan": { + "publisher": "microsoft-agci-gaming", + "product": "agci-gamedev-image", + "name": "[format('gamedev-{0}-{1}', parameters('gameEngine'), parameters('osType'))]" }, - "vmImage": "[variables('environments')[parameters('environment')].vmImage]", - "vmPlan": "[variables('environments')[parameters('environment')].vmPlan]", "vmName_var": "[parameters('vmName')]", "ipconfName": "[format('{0}-ipconf', variables('vmName_var'))]", "nicName_var": "[format('{0}-nic', variables('vmName_var'))]", diff --git a/application-workloads/azure-gamedev/gamedev-vm/nestedtemplates/gamedev-vm.bicep b/application-workloads/azure-gamedev/gamedev-vm/nestedtemplates/gamedev-vm.bicep index c4936937f979..80721a00b6a4 100644 --- a/application-workloads/azure-gamedev/gamedev-vm/nestedtemplates/gamedev-vm.bicep +++ b/application-workloads/azure-gamedev/gamedev-vm/nestedtemplates/gamedev-vm.bicep @@ -140,12 +140,6 @@ param publicIpNewOrExisting string = 'new' @description('Resource Group of the Public IP Address') param publicIpRGName string = resourceGroup().name -@allowed([ - 'development' - 'production' -]) -param environment string = 'production' - @description('Tags by resource.') param outTagsByResource object = {} @@ -162,44 +156,18 @@ param unrealPixelStreamingEnabled bool = false param enableManagedIdentity bool = false param enableAAD bool = false -var environmentMapping = { - ue_4_27: 'unreal_4_27' - ue_5_0ea: 'unreal_5_0ea' - unity_2020_3_19f1: 'unity_2020_3_19f1' +var vmImage = { + publisher: 'microsoft-agci-gaming' + offer: 'agci-gamedev-image' + sku: 'gamedev-${gameEngine}-${osType}' + version: 'latest' } - -var environments = { - development: { - vmImage: { - publisher: 'microsoft-agci-gaming' - offer: 'agci-gamedev-image' - sku: 'gamedev-${gameEngine}-${osType}' - version: 'latest' - } - vmPlan: { - publisher: 'microsoft-agci-gaming' - product: 'agci-gamedev-image' - name: 'gamedev-${gameEngine}-${osType}' - } - } - production: { - vmImage: { - publisher: 'microsoftcorporation1602274591143' - offer: 'game-dev-vm' - sku: '${osType}_${environmentMapping[gameEngine]}' - version: 'latest' - } - vmPlan: { - publisher: 'microsoftcorporation1602274591143' - product: 'game-dev-vm' - name: '${osType}_${environmentMapping[gameEngine]}' - } - } +var vmPlan = { + publisher: 'microsoft-agci-gaming' + product: 'agci-gamedev-image' + name: 'gamedev-${gameEngine}-${osType}' } -var vmImage = environments[environment].vmImage -var vmPlan = environments[environment].vmPlan - var vmName_var = vmName var ipconfName = '${vmName_var}-ipconf' var nicName_var = '${vmName_var}-nic' From 234055f9fbff0bdaeedfb7476e1fe2e61b120221 Mon Sep 17 00:00:00 2001 From: Daniel Ciborowski Date: Thu, 21 Apr 2022 15:14:38 -0400 Subject: [PATCH 30/71] Update gamedev-vm.bicep --- .../gamedev-vm/nestedtemplates/gamedev-vm.bicep | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/application-workloads/azure-gamedev/gamedev-vm/nestedtemplates/gamedev-vm.bicep b/application-workloads/azure-gamedev/gamedev-vm/nestedtemplates/gamedev-vm.bicep index 80721a00b6a4..179d3b7482e9 100644 --- a/application-workloads/azure-gamedev/gamedev-vm/nestedtemplates/gamedev-vm.bicep +++ b/application-workloads/azure-gamedev/gamedev-vm/nestedtemplates/gamedev-vm.bicep @@ -157,15 +157,15 @@ param enableManagedIdentity bool = false param enableAAD bool = false var vmImage = { - publisher: 'microsoft-agci-gaming' - offer: 'agci-gamedev-image' - sku: 'gamedev-${gameEngine}-${osType}' - version: 'latest' + "publisher": "microsoftcorporation1602274591143", + "offer": "game-dev-vm", + "sku": "[format('{0}_{1}', parameters('osType'), variables('environmentMapping')[parameters('gameEngine')])]", + "version": "latest" } var vmPlan = { - publisher: 'microsoft-agci-gaming' - product: 'agci-gamedev-image' - name: 'gamedev-${gameEngine}-${osType}' + "publisher": "microsoftcorporation1602274591143", + "product": "game-dev-vm", + "name": "[format('{0}_{1}', parameters('osType'), variables('environmentMapping')[parameters('gameEngine')])]" } var vmName_var = vmName From d9c99f79f850a1bbf81e73c2b4f482c345b94c13 Mon Sep 17 00:00:00 2001 From: Daniel Ciborowski Date: Thu, 21 Apr 2022 15:23:03 -0400 Subject: [PATCH 31/71] remove env options --- .../azure-gamedev/gamedev-vm/azuredeploy.json | 56 +++++++++++++++---- .../nestedtemplates/gamedev-vm.bicep | 49 +++++++++++++--- 2 files changed, 84 insertions(+), 21 deletions(-) diff --git a/application-workloads/azure-gamedev/gamedev-vm/azuredeploy.json b/application-workloads/azure-gamedev/gamedev-vm/azuredeploy.json index fbaee244aafe..f7c823775885 100644 --- a/application-workloads/azure-gamedev/gamedev-vm/azuredeploy.json +++ b/application-workloads/azure-gamedev/gamedev-vm/azuredeploy.json @@ -5,7 +5,7 @@ "_generator": { "name": "bicep", "version": "0.5.6.12127", - "templateHash": "748984281901363245" + "templateHash": "3569004983154617689" } }, "parameters": { @@ -124,7 +124,7 @@ "_generator": { "name": "bicep", "version": "0.5.6.12127", - "templateHash": "12443143369827810422" + "templateHash": "17557608924948392771" } }, "parameters": { @@ -416,6 +416,14 @@ "description": "Resource Group of the Public IP Address" } }, + "environment": { + "type": "string", + "defaultValue": "production", + "allowedValues": [ + "development", + "production" + ] + }, "outTagsByResource": { "type": "object", "defaultValue": {}, @@ -454,17 +462,41 @@ } }, "variables": { - "vmImage": { - "publisher": "microsoft-agci-gaming", - "offer": "agci-gamedev-image", - "sku": "[format('gamedev-{0}-{1}', parameters('gameEngine'), parameters('osType'))]", - "version": "latest" - }, - "vmPlan": { - "publisher": "microsoft-agci-gaming", - "product": "agci-gamedev-image", - "name": "[format('gamedev-{0}-{1}', parameters('gameEngine'), parameters('osType'))]" + "environmentMapping": { + "ue_4_27": "unreal_4_27", + "ue_5_0ea": "unreal_5_0ea", + "unity_2020_3_19f1": "unity_2020_3_19f1" + }, + "environments": { + "development": { + "vmImage": { + "publisher": "microsoft-agci-gaming", + "offer": "agci-gamedev-image", + "sku": "[format('gamedev-{0}-{1}', parameters('gameEngine'), parameters('osType'))]", + "version": "latest" + }, + "vmPlan": { + "publisher": "microsoft-agci-gaming", + "product": "agci-gamedev-image", + "name": "[format('gamedev-{0}-{1}', parameters('gameEngine'), parameters('osType'))]" + } + }, + "production": { + "vmImage": { + "publisher": "microsoftcorporation1602274591143", + "offer": "game-dev-vm", + "sku": "[format('{0}_{1}', parameters('osType'), variables('environmentMapping')[parameters('gameEngine')])]", + "version": "latest" + }, + "vmPlan": { + "publisher": "microsoftcorporation1602274591143", + "product": "game-dev-vm", + "name": "[format('{0}_{1}', parameters('osType'), variables('environmentMapping')[parameters('gameEngine')])]" + } + } }, + "vmImage": "[variables('environments')[parameters('environment')].vmImage]", + "vmPlan": "[variables('environments')[parameters('environment')].vmPlan]", "vmName_var": "[parameters('vmName')]", "ipconfName": "[format('{0}-ipconf', variables('vmName_var'))]", "nicName_var": "[format('{0}-nic', variables('vmName_var'))]", diff --git a/application-workloads/azure-gamedev/gamedev-vm/nestedtemplates/gamedev-vm.bicep b/application-workloads/azure-gamedev/gamedev-vm/nestedtemplates/gamedev-vm.bicep index 179d3b7482e9..200a467f886d 100644 --- a/application-workloads/azure-gamedev/gamedev-vm/nestedtemplates/gamedev-vm.bicep +++ b/application-workloads/azure-gamedev/gamedev-vm/nestedtemplates/gamedev-vm.bicep @@ -140,6 +140,12 @@ param publicIpNewOrExisting string = 'new' @description('Resource Group of the Public IP Address') param publicIpRGName string = resourceGroup().name +@allowed([ + 'development' + 'production' +]) +param environment string = 'production' + @description('Tags by resource.') param outTagsByResource object = {} @@ -156,17 +162,42 @@ param unrealPixelStreamingEnabled bool = false param enableManagedIdentity bool = false param enableAAD bool = false -var vmImage = { - "publisher": "microsoftcorporation1602274591143", - "offer": "game-dev-vm", - "sku": "[format('{0}_{1}', parameters('osType'), variables('environmentMapping')[parameters('gameEngine')])]", - "version": "latest" +var environmentMapping = { + ue_4_27: 'unreal_4_27' + ue_5_0ea: 'unreal_5_0ea' + unity_2020_3_19f1: 'unity_2020_3_19f1' } -var vmPlan = { - "publisher": "microsoftcorporation1602274591143", - "product": "game-dev-vm", - "name": "[format('{0}_{1}', parameters('osType'), variables('environmentMapping')[parameters('gameEngine')])]" + +var environments = { + development: { + vmImage: { + publisher: 'microsoft-agci-gaming' + offer: 'agci-gamedev-image' + sku: 'gamedev-${gameEngine}-${osType}' + version: 'latest' + } + vmPlan: { + publisher: 'microsoft-agci-gaming' + product: 'agci-gamedev-image' + name: 'gamedev-${gameEngine}-${osType}' + } + } + production: { + vmImage: { + publisher: 'microsoftcorporation1602274591143' + offer: 'game-dev-vm' + sku: '${osType}_${environmentMapping[gameEngine]}' + version: 'latest' + } + vmPlan: { + publisher: 'microsoftcorporation1602274591143' + product: 'game-dev-vm' + name: '${osType}_${environmentMapping[gameEngine]}' + } + } } +var vmImage = environments[environment].vmImage +var vmPlan = environments[environment].vmPlan var vmName_var = vmName var ipconfName = '${vmName_var}-ipconf' From 41a7b3eb79fa1f413a6678793eca069f4293db55 Mon Sep 17 00:00:00 2001 From: Daniel Ciborowski Date: Thu, 21 Apr 2022 17:49:30 -0500 Subject: [PATCH 32/71] Add files via upload --- .../gamedev-vm/scripts/PreInstall.zip | Bin 0 -> 73153 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 application-workloads/azure-gamedev/gamedev-vm/scripts/PreInstall.zip diff --git a/application-workloads/azure-gamedev/gamedev-vm/scripts/PreInstall.zip b/application-workloads/azure-gamedev/gamedev-vm/scripts/PreInstall.zip new file mode 100644 index 0000000000000000000000000000000000000000..7e3049782eb6f2c5c239b6815922a9a1a2afd334 GIT binary patch literal 73153 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�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 literal 0 HcmV?d00001 From 04f05930283a6ccc6e1c791f69ecd5afe8a33ef8 Mon Sep 17 00:00:00 2001 From: Daniel Ciborowski Date: Thu, 21 Apr 2022 19:15:09 -0400 Subject: [PATCH 33/71] Update README.md --- application-workloads/azure-gamedev/gamedev-vm/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/application-workloads/azure-gamedev/gamedev-vm/README.md b/application-workloads/azure-gamedev/gamedev-vm/README.md index 59558b4fc0cb..72ec2e3a7765 100644 --- a/application-workloads/azure-gamedev/gamedev-vm/README.md +++ b/application-workloads/azure-gamedev/gamedev-vm/README.md @@ -13,3 +13,4 @@ [![Deploy To Azure](https://raw.githubusercontent.com/Azure/azure-quickstart-templates/master/1-CONTRIBUTION-GUIDE/images/deploytoazure.svg?sanitize=true)](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2FAzure%2Fazure-quickstart-templates%2Fmaster%2Fapplication-workloads%2Fazure-gamedev%2Fgamedev-vm%2Fazuredeploy.json) [![Deploy To Azure US Gov](https://raw.githubusercontent.com/Azure/azure-quickstart-templates/master/1-CONTRIBUTION-GUIDE/images/deploytoazuregov.svg?sanitize=true)](https://portal.azure.us/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2FAzure%2Fazure-quickstart-templates%2Fmaster%2Fapplication-workloads%2Fazure-gamedev%2Fgamedev-vm%2Fazuredeploy.json) [![Visualize](https://raw.githubusercontent.com/Azure/azure-quickstart-templates/master/1-CONTRIBUTION-GUIDE/images/visualizebutton.svg?sanitize=true)](http://armviz.io/#/?load=https%3A%2F%2Fraw.githubusercontent.com%2FAzure%2Fazure-quickstart-templates%2Fmaster%2Fapplication-workloads%2Fazure-gamedev%2Fgamedev-vm%2Fazuredeploy.json) + From 212f67af09f0939013154c23c179303f2ee16b99 Mon Sep 17 00:00:00 2001 From: Daniel Ciborowski Date: Fri, 22 Apr 2022 09:50:46 -0400 Subject: [PATCH 34/71] Apply suggestions from code review --- .../azure-gamedev/gamedev-vm/azuredeploy.parameters.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application-workloads/azure-gamedev/gamedev-vm/azuredeploy.parameters.json b/application-workloads/azure-gamedev/gamedev-vm/azuredeploy.parameters.json index 7714ab04875b..1e5bbf5be28b 100644 --- a/application-workloads/azure-gamedev/gamedev-vm/azuredeploy.parameters.json +++ b/application-workloads/azure-gamedev/gamedev-vm/azuredeploy.parameters.json @@ -6,7 +6,7 @@ "value": "GEN-UNIQUE" }, "passwordAdministratorLogin": { - "value": "GEN-UNIQUE" + "value": ""GEN-SSH-PUB-KEY"" }, "vmSize": { "value": "Standard_NV12s_v3" From 32679a2979493daa5555f887f3c236c99acf104a Mon Sep 17 00:00:00 2001 From: Daniel Ciborowski Date: Fri, 22 Apr 2022 09:53:12 -0400 Subject: [PATCH 35/71] Update metadata.json --- application-workloads/azure-gamedev/gamedev-vm/metadata.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/application-workloads/azure-gamedev/gamedev-vm/metadata.json b/application-workloads/azure-gamedev/gamedev-vm/metadata.json index a3d3795897d7..a110e4a736a3 100644 --- a/application-workloads/azure-gamedev/gamedev-vm/metadata.json +++ b/application-workloads/azure-gamedev/gamedev-vm/metadata.json @@ -5,5 +5,8 @@ "description": "Azure Game Developer Virtual Machine includes Licencsed Engines like Unreal.", "summary": "This template deploys an Azure Game Developer Virtual Machine.", "githubUsername": "dciborow", - "dateUpdated": "2022-04-01" + "dateUpdated": "2022-04-01", + "environments": [ + "AzureCloud" + ] } From 199be0b2de919b100ccfa7ab8c67c2c85e1a9a95 Mon Sep 17 00:00:00 2001 From: Daniel Ciborowski Date: Fri, 22 Apr 2022 09:54:06 -0400 Subject: [PATCH 36/71] Update application-workloads/azure-gamedev/gamedev-vm/azuredeploy.parameters.json --- .../azure-gamedev/gamedev-vm/azuredeploy.parameters.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application-workloads/azure-gamedev/gamedev-vm/azuredeploy.parameters.json b/application-workloads/azure-gamedev/gamedev-vm/azuredeploy.parameters.json index 1e5bbf5be28b..9176e198d0fd 100644 --- a/application-workloads/azure-gamedev/gamedev-vm/azuredeploy.parameters.json +++ b/application-workloads/azure-gamedev/gamedev-vm/azuredeploy.parameters.json @@ -6,7 +6,7 @@ "value": "GEN-UNIQUE" }, "passwordAdministratorLogin": { - "value": ""GEN-SSH-PUB-KEY"" + "value": "GEN-PASSWORD " }, "vmSize": { "value": "Standard_NV12s_v3" From 566f53726d23f08ccd9fbe79bcbff7ddc52381f9 Mon Sep 17 00:00:00 2001 From: Daniel Ciborowski Date: Fri, 22 Apr 2022 18:01:48 -0400 Subject: [PATCH 37/71] Update README.md --- application-workloads/azure-gamedev/gamedev-vm/README.md | 5 ----- 1 file changed, 5 deletions(-) diff --git a/application-workloads/azure-gamedev/gamedev-vm/README.md b/application-workloads/azure-gamedev/gamedev-vm/README.md index 72ec2e3a7765..8e99b5cbdf20 100644 --- a/application-workloads/azure-gamedev/gamedev-vm/README.md +++ b/application-workloads/azure-gamedev/gamedev-vm/README.md @@ -2,15 +2,10 @@ ![Azure Public Test Date](https://azurequickstartsservice.blob.core.windows.net/badges/application-workloads/azure-gamedev/gamedev-vm/PublicLastTestDate.svg) ![Azure Public Test Result](https://azurequickstartsservice.blob.core.windows.net/badges/application-workloads/azure-gamedev/gamedev-vm/PublicDeployment.svg) -![Azure US Gov Last Test Date](https://azurequickstartsservice.blob.core.windows.net/badges/application-workloads/azure-gamedev/gamedev-vm/FairfaxLastTestDate.svg) -![Azure US Gov Last Test Result](https://azurequickstartsservice.blob.core.windows.net/badges/application-workloads/azure-gamedev/gamedev-vm/FairfaxDeployment.svg) - ![Best Practice Check](https://azurequickstartsservice.blob.core.windows.net/badges/application-workloads/azure-gamedev/gamedev-vm/BestPracticeResult.svg) ![Cred Scan Check](https://azurequickstartsservice.blob.core.windows.net/badges/application-workloads/azure-gamedev/gamedev-vm/CredScanResult.svg) ![Bicep Version](https://azurequickstartsservice.blob.core.windows.net/badges/application-workloads/azure-gamedev/gamedev-vm/BicepVersion.svg) [![Deploy To Azure](https://raw.githubusercontent.com/Azure/azure-quickstart-templates/master/1-CONTRIBUTION-GUIDE/images/deploytoazure.svg?sanitize=true)](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2FAzure%2Fazure-quickstart-templates%2Fmaster%2Fapplication-workloads%2Fazure-gamedev%2Fgamedev-vm%2Fazuredeploy.json) -[![Deploy To Azure US Gov](https://raw.githubusercontent.com/Azure/azure-quickstart-templates/master/1-CONTRIBUTION-GUIDE/images/deploytoazuregov.svg?sanitize=true)](https://portal.azure.us/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2FAzure%2Fazure-quickstart-templates%2Fmaster%2Fapplication-workloads%2Fazure-gamedev%2Fgamedev-vm%2Fazuredeploy.json) [![Visualize](https://raw.githubusercontent.com/Azure/azure-quickstart-templates/master/1-CONTRIBUTION-GUIDE/images/visualizebutton.svg?sanitize=true)](http://armviz.io/#/?load=https%3A%2F%2Fraw.githubusercontent.com%2FAzure%2Fazure-quickstart-templates%2Fmaster%2Fapplication-workloads%2Fazure-gamedev%2Fgamedev-vm%2Fazuredeploy.json) - From e77837d64e78efbe83f0ac4e71ba34c50d0859c8 Mon Sep 17 00:00:00 2001 From: Daniel Ciborowski Date: Fri, 22 Apr 2022 18:02:11 -0400 Subject: [PATCH 38/71] Update application-workloads/azure-gamedev/gamedev-vm/azuredeploy.parameters.json --- .../azure-gamedev/gamedev-vm/azuredeploy.parameters.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application-workloads/azure-gamedev/gamedev-vm/azuredeploy.parameters.json b/application-workloads/azure-gamedev/gamedev-vm/azuredeploy.parameters.json index 9176e198d0fd..ab35cd429383 100644 --- a/application-workloads/azure-gamedev/gamedev-vm/azuredeploy.parameters.json +++ b/application-workloads/azure-gamedev/gamedev-vm/azuredeploy.parameters.json @@ -6,7 +6,7 @@ "value": "GEN-UNIQUE" }, "passwordAdministratorLogin": { - "value": "GEN-PASSWORD " + "value": "GEN-PASSWORD" }, "vmSize": { "value": "Standard_NV12s_v3" From 4cc5407d7cecb4ea52cc6aea15c6fd1f7291d65d Mon Sep 17 00:00:00 2001 From: Daniel Ciborowski Date: Fri, 22 Apr 2022 18:03:45 -0400 Subject: [PATCH 39/71] Update application-workloads/azure-gamedev/gamedev-vm/azuredeploy.parameters.json --- .../azure-gamedev/gamedev-vm/azuredeploy.parameters.json | 3 --- 1 file changed, 3 deletions(-) diff --git a/application-workloads/azure-gamedev/gamedev-vm/azuredeploy.parameters.json b/application-workloads/azure-gamedev/gamedev-vm/azuredeploy.parameters.json index ab35cd429383..08fc8021eeb5 100644 --- a/application-workloads/azure-gamedev/gamedev-vm/azuredeploy.parameters.json +++ b/application-workloads/azure-gamedev/gamedev-vm/azuredeploy.parameters.json @@ -7,9 +7,6 @@ }, "passwordAdministratorLogin": { "value": "GEN-PASSWORD" - }, - "vmSize": { - "value": "Standard_NV12s_v3" } } } \ No newline at end of file From f2eb003ef5a3bd7a3198bce0033f4742b6af5422 Mon Sep 17 00:00:00 2001 From: Daniel Ciborowski Date: Fri, 22 Apr 2022 18:11:36 -0400 Subject: [PATCH 40/71] Update README.md --- application-workloads/azure-gamedev/gamedev-vm/README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/application-workloads/azure-gamedev/gamedev-vm/README.md b/application-workloads/azure-gamedev/gamedev-vm/README.md index 8e99b5cbdf20..d91b1b95bcfc 100644 --- a/application-workloads/azure-gamedev/gamedev-vm/README.md +++ b/application-workloads/azure-gamedev/gamedev-vm/README.md @@ -1,11 +1,14 @@ -# Setup Azure Game Development Virtual Machine ![Azure Public Test Date](https://azurequickstartsservice.blob.core.windows.net/badges/application-workloads/azure-gamedev/gamedev-vm/PublicLastTestDate.svg) ![Azure Public Test Result](https://azurequickstartsservice.blob.core.windows.net/badges/application-workloads/azure-gamedev/gamedev-vm/PublicDeployment.svg) +![Azure US Gov Last Test Date](https://azurequickstartsservice.blob.core.windows.net/badges/application-workloads/azure-gamedev/gamedev-vm/FairfaxLastTestDate.svg) +![Azure US Gov Last Test Result](https://azurequickstartsservice.blob.core.windows.net/badges/application-workloads/azure-gamedev/gamedev-vm/FairfaxDeployment.svg) + ![Best Practice Check](https://azurequickstartsservice.blob.core.windows.net/badges/application-workloads/azure-gamedev/gamedev-vm/BestPracticeResult.svg) ![Cred Scan Check](https://azurequickstartsservice.blob.core.windows.net/badges/application-workloads/azure-gamedev/gamedev-vm/CredScanResult.svg) ![Bicep Version](https://azurequickstartsservice.blob.core.windows.net/badges/application-workloads/azure-gamedev/gamedev-vm/BicepVersion.svg) [![Deploy To Azure](https://raw.githubusercontent.com/Azure/azure-quickstart-templates/master/1-CONTRIBUTION-GUIDE/images/deploytoazure.svg?sanitize=true)](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2FAzure%2Fazure-quickstart-templates%2Fmaster%2Fapplication-workloads%2Fazure-gamedev%2Fgamedev-vm%2Fazuredeploy.json) + [![Visualize](https://raw.githubusercontent.com/Azure/azure-quickstart-templates/master/1-CONTRIBUTION-GUIDE/images/visualizebutton.svg?sanitize=true)](http://armviz.io/#/?load=https%3A%2F%2Fraw.githubusercontent.com%2FAzure%2Fazure-quickstart-templates%2Fmaster%2Fapplication-workloads%2Fazure-gamedev%2Fgamedev-vm%2Fazuredeploy.json) From e20dc7ddf49318fcfae901c5eaeb1a1383cc592d Mon Sep 17 00:00:00 2001 From: Daniel Ciborowski Date: Fri, 22 Apr 2022 18:21:07 -0400 Subject: [PATCH 41/71] Update README.md --- application-workloads/azure-gamedev/gamedev-vm/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/application-workloads/azure-gamedev/gamedev-vm/README.md b/application-workloads/azure-gamedev/gamedev-vm/README.md index d91b1b95bcfc..d702fb4cb20b 100644 --- a/application-workloads/azure-gamedev/gamedev-vm/README.md +++ b/application-workloads/azure-gamedev/gamedev-vm/README.md @@ -1,3 +1,4 @@ +# Setup Azure Game Development Virtual Machine ![Azure Public Test Date](https://azurequickstartsservice.blob.core.windows.net/badges/application-workloads/azure-gamedev/gamedev-vm/PublicLastTestDate.svg) ![Azure Public Test Result](https://azurequickstartsservice.blob.core.windows.net/badges/application-workloads/azure-gamedev/gamedev-vm/PublicDeployment.svg) From 38d22dbcd693cd71a6195732f2cccd4fbeb00d27 Mon Sep 17 00:00:00 2001 From: Daniel Ciborowski Date: Fri, 22 Apr 2022 18:49:41 -0400 Subject: [PATCH 42/71] cleanup bicep --- .../azure-gamedev/gamedev-vm/azuredeploy.json | 58 ++++++++++++++----- .../azure-gamedev/gamedev-vm/main.bicep | 9 ++- .../nestedtemplates/gamedev-vm.bicep | 7 ++- .../gamedev-vm/nestedtemplates/nsgRules.bicep | 1 + 4 files changed, 59 insertions(+), 16 deletions(-) diff --git a/application-workloads/azure-gamedev/gamedev-vm/azuredeploy.json b/application-workloads/azure-gamedev/gamedev-vm/azuredeploy.json index f7c823775885..8c1feb960b7c 100644 --- a/application-workloads/azure-gamedev/gamedev-vm/azuredeploy.json +++ b/application-workloads/azure-gamedev/gamedev-vm/azuredeploy.json @@ -5,13 +5,16 @@ "_generator": { "name": "bicep", "version": "0.5.6.12127", - "templateHash": "3569004983154617689" + "templateHash": "5803441896698059175" } }, "parameters": { "location": { "type": "string", - "defaultValue": "[resourceGroup().location]" + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Deployment Location" + } }, "_artifactsLocation": { "type": "string", @@ -33,7 +36,10 @@ "allowedValues": [ "ue_4_27", "ue_5_0ea" - ] + ], + "metadata": { + "description": "Select Game Engine Version" + } }, "osType": { "type": "string", @@ -41,7 +47,10 @@ "allowedValues": [ "win10", "ws2019" - ] + ], + "metadata": { + "description": "Select Operating System" + } }, "vmSize": { "type": "string", @@ -57,13 +66,22 @@ "Standard_NV12s_v3", "Standard_NV24s_v3", "Standard_NV48s_v3" - ] + ], + "metadata": { + "description": "Select Virtual Machine Skew" + } }, "administratorLogin": { - "type": "string" + "type": "string", + "metadata": { + "description": "Administrator Login for access" + } }, "passwordAdministratorLogin": { - "type": "secureString" + "type": "secureString", + "metadata": { + "description": "Administrator Password for access" + } }, "remoteAccessTechnology": { "type": "string", @@ -124,7 +142,7 @@ "_generator": { "name": "bicep", "version": "0.5.6.12127", - "templateHash": "17557608924948392771" + "templateHash": "6895246921804627188" } }, "parameters": { @@ -422,7 +440,10 @@ "allowedValues": [ "development", "production" - ] + ], + "metadata": { + "description": "Select Image Deployment for debugging only" + } }, "outTagsByResource": { "type": "object", @@ -433,7 +454,7 @@ }, "_artifactsLocation": { "type": "string", - "defaultValue": "[deployment().properties.templateLink.uri]", + "defaultValue": "[deployment().properties.templateLink.id]", "metadata": { "description": "The base URI where artifacts required by this template are located including a trailing '/'" } @@ -454,11 +475,17 @@ }, "enableManagedIdentity": { "type": "bool", - "defaultValue": false + "defaultValue": false, + "metadata": { + "description": "Enable Managed Identity" + } }, "enableAAD": { "type": "bool", - "defaultValue": false + "defaultValue": false, + "metadata": { + "description": "Enable Azure Active Directory Login" + } } }, "variables": { @@ -697,13 +724,16 @@ "_generator": { "name": "bicep", "version": "0.5.6.12127", - "templateHash": "16879828913748412764" + "templateHash": "8432771765242177029" } }, "parameters": { "addPixelStreamingPorts": { "type": "bool", - "defaultValue": false + "defaultValue": false, + "metadata": { + "description": "Enable Pick Streaming" + } } }, "variables": { diff --git a/application-workloads/azure-gamedev/gamedev-vm/main.bicep b/application-workloads/azure-gamedev/gamedev-vm/main.bicep index 5ce924258853..e87d0f524a58 100644 --- a/application-workloads/azure-gamedev/gamedev-vm/main.bicep +++ b/application-workloads/azure-gamedev/gamedev-vm/main.bicep @@ -1,4 +1,4 @@ - +@description('Deployment Location') param location string = resourceGroup().location @description('The base URI where artifacts required by this template are located including a trailing \'/\'') @@ -8,17 +8,21 @@ param _artifactsLocation string = deployment().properties.templateLink.uri @secure() param _artifactsLocationSasToken string = '' +@description('Select Game Engine Version') @allowed([ 'ue_4_27' 'ue_5_0ea' ]) param gameEngine string = 'ue_4_27' + +@description('Select Operating System') @allowed([ 'win10' 'ws2019' ]) param osType string = 'win10' +@description('Select Virtual Machine Skew') @allowed([ 'Standard_NC4as_T4_v3' 'Standard_NC8as_T4_v3' @@ -33,7 +37,10 @@ param osType string = 'win10' ]) param vmSize string = 'Standard_NV12s_v3' +@description('Administrator Login for access') param administratorLogin string + +@description('Administrator Password for access') @secure() param passwordAdministratorLogin string diff --git a/application-workloads/azure-gamedev/gamedev-vm/nestedtemplates/gamedev-vm.bicep b/application-workloads/azure-gamedev/gamedev-vm/nestedtemplates/gamedev-vm.bicep index 200a467f886d..d101313657f9 100644 --- a/application-workloads/azure-gamedev/gamedev-vm/nestedtemplates/gamedev-vm.bicep +++ b/application-workloads/azure-gamedev/gamedev-vm/nestedtemplates/gamedev-vm.bicep @@ -140,6 +140,7 @@ param publicIpNewOrExisting string = 'new' @description('Resource Group of the Public IP Address') param publicIpRGName string = resourceGroup().name +@description('Select Image Deployment for debugging only') @allowed([ 'development' 'production' @@ -150,7 +151,7 @@ param environment string = 'production' param outTagsByResource object = {} @description('The base URI where artifacts required by this template are located including a trailing \'/\'') -param _artifactsLocation string = deployment().properties.templateLink.uri +param _artifactsLocation string = deployment().properties.templateLink.id @description('The sasToken required to access _artifactsLocation.') @secure() @@ -159,7 +160,10 @@ param _artifactsLocationSasToken string = '' @description('Enable or disable Unreal Pixel Streaming port.') param unrealPixelStreamingEnabled bool = false +@description('Enable Managed Identity') param enableManagedIdentity bool = false + +@description('Enable Azure Active Directory Login') param enableAAD bool = false var environmentMapping = { @@ -196,6 +200,7 @@ var environments = { } } } + var vmImage = environments[environment].vmImage var vmPlan = environments[environment].vmPlan diff --git a/application-workloads/azure-gamedev/gamedev-vm/nestedtemplates/nsgRules.bicep b/application-workloads/azure-gamedev/gamedev-vm/nestedtemplates/nsgRules.bicep index 8560bbc14aa8..fe254e912920 100644 --- a/application-workloads/azure-gamedev/gamedev-vm/nestedtemplates/nsgRules.bicep +++ b/application-workloads/azure-gamedev/gamedev-vm/nestedtemplates/nsgRules.bicep @@ -1,3 +1,4 @@ +@description('Enable Pick Streaming') param addPixelStreamingPorts bool = false var nsgRules = { From 0145c433cf961e6d09f3048fdb67a33835997195 Mon Sep 17 00:00:00 2001 From: Daniel Ciborowski Date: Fri, 22 Apr 2022 21:09:48 -0400 Subject: [PATCH 43/71] Rename createDataDisk.ps1 to CreateDataDisk.ps1 --- .../scripts/{createDataDisk.ps1 => CreateDataDisk.ps1} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename application-workloads/azure-gamedev/gamedev-vm/scripts/{createDataDisk.ps1 => CreateDataDisk.ps1} (96%) diff --git a/application-workloads/azure-gamedev/gamedev-vm/scripts/createDataDisk.ps1 b/application-workloads/azure-gamedev/gamedev-vm/scripts/CreateDataDisk.ps1 similarity index 96% rename from application-workloads/azure-gamedev/gamedev-vm/scripts/createDataDisk.ps1 rename to application-workloads/azure-gamedev/gamedev-vm/scripts/CreateDataDisk.ps1 index 3d6ef434c5f1..e3ffba1d7e37 100644 --- a/application-workloads/azure-gamedev/gamedev-vm/scripts/createDataDisk.ps1 +++ b/application-workloads/azure-gamedev/gamedev-vm/scripts/CreateDataDisk.ps1 @@ -16,4 +16,4 @@ New-Partition -DiskNumber $diskNumber -UseMaximumSize -AssignDriveLetter Start-Sleep -Seconds 20 -Format-Volume -DriveLetter F -FileSystem NTFS -NewFileSystemLabel Data -Confirm:$false -Force \ No newline at end of file +Format-Volume -DriveLetter F -FileSystem NTFS -NewFileSystemLabel Data -Confirm:$false -Force From e7f3a01fd78650ba84ae9ad916bdb3d58d3746c2 Mon Sep 17 00:00:00 2001 From: Daniel Ciborowski Date: Fri, 22 Apr 2022 21:12:21 -0400 Subject: [PATCH 44/71] Rename MountFIleShare.ps1 to MountFileShare.ps1 --- .../scripts/{MountFIleShare.ps1 => MountFileShare.ps1} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename application-workloads/azure-gamedev/gamedev-vm/scripts/{MountFIleShare.ps1 => MountFileShare.ps1} (99%) diff --git a/application-workloads/azure-gamedev/gamedev-vm/scripts/MountFIleShare.ps1 b/application-workloads/azure-gamedev/gamedev-vm/scripts/MountFileShare.ps1 similarity index 99% rename from application-workloads/azure-gamedev/gamedev-vm/scripts/MountFIleShare.ps1 rename to application-workloads/azure-gamedev/gamedev-vm/scripts/MountFileShare.ps1 index fc858a5d20de..a91005d36957 100644 --- a/application-workloads/azure-gamedev/gamedev-vm/scripts/MountFIleShare.ps1 +++ b/application-workloads/azure-gamedev/gamedev-vm/scripts/MountFileShare.ps1 @@ -9,4 +9,4 @@ if ($storageAccount -and $storageAccountKey -and $fileShareName) Add-Content 'C:\\ProgramData\\Microsoft\\Windows\\Start Menu\\Programs\\Startup\\mountfileshare.cmd' "cmdkey /add:$storageAccount.file.core.windows.net /user:localhost\$storageAccount /pass:`"$storageAccountKey`"" Add-Content 'C:\\ProgramData\\Microsoft\\Windows\\Start Menu\\Programs\\Startup\\mountfileshare.cmd' "net use S: `"\\$storageAccount.file.core.windows.net\$fileShareName`" /persistent:yes" Add-Content 'C:\\ProgramData\\Microsoft\\Windows\\Start Menu\\Programs\\Startup\\mountfileshare.cmd' "del `"C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Startup\mountfileshare.cmd`"" -} \ No newline at end of file +} From 3589e58138588c5d52901ecf78ed1895b698909d Mon Sep 17 00:00:00 2001 From: Daniel Ciborowski Date: Fri, 22 Apr 2022 21:16:18 -0400 Subject: [PATCH 45/71] Create PostInstall.ps1 --- .../gamedev-vm/scripts/PostInstall.ps1 | 1022 +++++++++++++++++ 1 file changed, 1022 insertions(+) create mode 100644 application-workloads/azure-gamedev/gamedev-vm/scripts/PostInstall.ps1 diff --git a/application-workloads/azure-gamedev/gamedev-vm/scripts/PostInstall.ps1 b/application-workloads/azure-gamedev/gamedev-vm/scripts/PostInstall.ps1 new file mode 100644 index 000000000000..9d064b18b9ed --- /dev/null +++ b/application-workloads/azure-gamedev/gamedev-vm/scripts/PostInstall.ps1 @@ -0,0 +1,1022 @@ +param ( + [switch]$DontPromptPasswordUpdateGPU + ) + + +$host.ui.RawUI.WindowTitle = "Parsec Cloud Preparation Tool" + +[Net.ServicePointManager]::SecurityProtocol = "tls12, tls11, tls" + +$logsfolder = "C:\logs" +$logoutput = $logsfolder + '\setup-output-' + (get-date).ToString('MMddyyhhmmss') + '.txt' +New-Item -Path $logsfolder -ItemType directory -Force + +Function ProgressWriter { + param ( + [int]$percentcomplete, + [string]$status + ) + $output = "$MessageTime - $percentcomplete - $status" + Write-Progress -Activity "Setting Up Your Machine" -Status $status -PercentComplete $PercentComplete + Add-Content -Path $logoutput -Value $output +} + +#$path = [Environment]::GetFolderPath("Desktop") +$path = "C:" +# $currentusersid = Get-LocalUser "$env:USERNAME" | Select-Object SID | ft -HideTableHeaders | Out-String | ForEach-Object { $_.Trim() } + +#Creating Folders and moving script files into System directories +function setupEnvironment { + ProgressWriter -Status "Moving files and folders into place" -PercentComplete $PercentComplete + if((Test-Path -Path C:\Windows\system32\GroupPolicy\Machine\Scripts\Startup) -eq $true) {} Else {New-Item -Path C:\Windows\system32\GroupPolicy\Machine\Scripts\Startup -ItemType directory | Out-Null} + if((Test-Path -Path C:\Windows\system32\GroupPolicy\Machine\Scripts\Shutdown) -eq $true) {} Else {New-Item -Path C:\Windows\system32\GroupPolicy\Machine\Scripts\Shutdown -ItemType directory | Out-Null} + if((Test-Path -Path $env:ProgramData\ParsecLoader) -eq $true) {} Else {New-Item -Path $env:ProgramData\ParsecLoader -ItemType directory | Out-Null} + if((Test-Path C:\Windows\system32\GroupPolicy\Machine\Scripts\psscripts.ini) -eq $true) {} Else {Move-Item -Path $path\ParsecTemp\PreInstall\psscripts.ini -Destination C:\Windows\system32\GroupPolicy\Machine\Scripts} + if((Test-Path C:\Windows\system32\GroupPolicy\Machine\Scripts\Shutdown\NetworkRestore.ps1) -eq $true) {} Else {Move-Item -Path $path\ParsecTemp\PreInstall\NetworkRestore.ps1 -Destination C:\Windows\system32\GroupPolicy\Machine\Scripts\Shutdown} + if((Test-Path $env:ProgramData\ParsecLoader\clear-proxy.ps1) -eq $true) {} Else {Move-Item -Path $path\ParsecTemp\PreInstall\clear-proxy.ps1 -Destination $env:ProgramData\ParsecLoader} + if((Test-Path $env:ProgramData\ParsecLoader\CreateClearProxyScheduledTask.ps1) -eq $true) {} Else {Move-Item -Path $path\ParsecTemp\PreInstall\CreateClearProxyScheduledTask.ps1 -Destination $env:ProgramData\ParsecLoader} + if((Test-Path $env:ProgramData\ParsecLoader\Automatic-Shutdown.ps1) -eq $true) {} Else {Move-Item -Path $path\ParsecTemp\PreInstall\Automatic-Shutdown.ps1 -Destination $env:ProgramData\ParsecLoader} + if((Test-Path $env:ProgramData\ParsecLoader\CreateAutomaticShutdownScheduledTask.ps1) -eq $true) {} Else {Move-Item -Path $path\ParsecTemp\PreInstall\CreateAutomaticShutdownScheduledTask.ps1 -Destination $env:ProgramData\ParsecLoader} + if((Test-Path $env:ProgramData\ParsecLoader\GPU-Update.ico) -eq $true) {} Else {Move-Item -Path $path\ParsecTemp\PreInstall\GPU-Update.ico -Destination $env:ProgramData\ParsecLoader} + if((Test-Path $env:ProgramData\ParsecLoader\CreateOneHourWarningScheduledTask.ps1) -eq $true) {} Else {Move-Item -Path $path\ParsecTemp\PreInstall\CreateOneHourWarningScheduledTask.ps1 -Destination $env:ProgramData\ParsecLoader} + if((Test-Path $env:ProgramData\ParsecLoader\WarningMessage.ps1) -eq $true) {} Else {Move-Item -Path $path\ParsecTemp\PreInstall\WarningMessage.ps1 -Destination $env:ProgramData\ParsecLoader} + if((Test-Path $env:ProgramData\ParsecLoader\Parsec.png) -eq $true) {} Else {Move-Item -Path $path\ParsecTemp\PreInstall\Parsec.png -Destination $env:ProgramData\ParsecLoader} + if((Test-Path $env:ProgramData\ParsecLoader\ShowDialog.ps1) -eq $true) {} Else {Move-Item -Path $path\ParsecTemp\PreInstall\ShowDialog.ps1 -Destination $env:ProgramData\ParsecLoader} + if((Test-Path $env:ProgramData\ParsecLoader\OneHour.ps1) -eq $true) {} Else {Move-Item -Path $path\ParsecTemp\PreInstall\OneHour.ps1 -Destination $env:ProgramData\ParsecLoader} + if((Test-Path $env:ProgramData\ParsecLoader\TeamMachineSetup.ps1) -eq $true) {} Else {Move-Item -Path $path\ParsecTemp\PreInstall\TeamMachineSetup.ps1 -Destination $env:ProgramData\ParsecLoader} + } + +function cloudprovider { + #finds the cloud provider that this VM is hosted by + $gcp = $( + try { + (Invoke-WebRequest -uri http://metadata.google.internal/computeMetadata/v1/ -Method GET -header @{'metadata-flavor'='Google'} -TimeoutSec 5) + } + catch { + } + ) + + $aws = $( + Try { + (Invoke-WebRequest -uri http://169.254.169.254/latest/meta-data/ -TimeoutSec 5) + } + catch { + } + ) + + $paperspace = $( + Try { + (Invoke-WebRequest -uri http://metadata.paperspace.com/meta-data/machine -TimeoutSec 5) + } + catch { + } + ) + + $azure = $( + Try {(Invoke-WebRequest -Uri "http://169.254.169.254/metadata/instance?api-version=2018-10-01" -Headers @{Metadata="true"} -TimeoutSec 5)} + catch {} + ) + + + if ($GCP.StatusCode -eq 200) { + "Google Cloud Instance" + } + Elseif ($AWS.StatusCode -eq 200) { + "Amazon AWS Instance" + } + Elseif ($paperspace.StatusCode -eq 200) { + "Paperspace Instance" + } + Elseif ($azure.StatusCode -eq 200) { + "Microsoft Azure Instance" + } + Else { + "Generic Instance" + } +} + + +add-type @" + using System; + using System.Collections.Generic; + using System.Text; + using System.Runtime.InteropServices; + + namespace ComputerSystem + { + public class LSAutil + { + [StructLayout(LayoutKind.Sequential)] + private struct LSA_UNICODE_STRING + { + public UInt16 Length; + public UInt16 MaximumLength; + public IntPtr Buffer; + } + + [StructLayout(LayoutKind.Sequential)] + private struct LSA_OBJECT_ATTRIBUTES + { + public int Length; + public IntPtr RootDirectory; + public LSA_UNICODE_STRING ObjectName; + public uint Attributes; + public IntPtr SecurityDescriptor; + public IntPtr SecurityQualityOfService; + } + + private enum LSA_AccessPolicy : long + { + POLICY_VIEW_LOCAL_INFORMATION = 0x00000001L, + POLICY_VIEW_AUDIT_INFORMATION = 0x00000002L, + POLICY_GET_PRIVATE_INFORMATION = 0x00000004L, + POLICY_TRUST_ADMIN = 0x00000008L, + POLICY_CREATE_ACCOUNT = 0x00000010L, + POLICY_CREATE_SECRET = 0x00000020L, + POLICY_CREATE_PRIVILEGE = 0x00000040L, + POLICY_SET_DEFAULT_QUOTA_LIMITS = 0x00000080L, + POLICY_SET_AUDIT_REQUIREMENTS = 0x00000100L, + POLICY_AUDIT_LOG_ADMIN = 0x00000200L, + POLICY_SERVER_ADMIN = 0x00000400L, + POLICY_LOOKUP_NAMES = 0x00000800L, + POLICY_NOTIFICATION = 0x00001000L + } + + [DllImport("advapi32.dll", SetLastError = true, PreserveSig = true)] + private static extern uint LsaRetrievePrivateData( + IntPtr PolicyHandle, + ref LSA_UNICODE_STRING KeyName, + out IntPtr PrivateData + ); + + [DllImport("advapi32.dll", SetLastError = true, PreserveSig = true)] + private static extern uint LsaStorePrivateData( + IntPtr policyHandle, + ref LSA_UNICODE_STRING KeyName, + ref LSA_UNICODE_STRING PrivateData + ); + + [DllImport("advapi32.dll", SetLastError = true, PreserveSig = true)] + private static extern uint LsaOpenPolicy( + ref LSA_UNICODE_STRING SystemName, + ref LSA_OBJECT_ATTRIBUTES ObjectAttributes, + uint DesiredAccess, + out IntPtr PolicyHandle + ); + + [DllImport("advapi32.dll", SetLastError = true, PreserveSig = true)] + private static extern uint LsaNtStatusToWinError( + uint status + ); + + [DllImport("advapi32.dll", SetLastError = true, PreserveSig = true)] + private static extern uint LsaClose( + IntPtr policyHandle + ); + + [DllImport("advapi32.dll", SetLastError = true, PreserveSig = true)] + private static extern uint LsaFreeMemory( + IntPtr buffer + ); + + private LSA_OBJECT_ATTRIBUTES objectAttributes; + private LSA_UNICODE_STRING localsystem; + private LSA_UNICODE_STRING secretName; + + public LSAutil(string key) + { + if (key.Length == 0) + { + throw new Exception("Key lenght zero"); + } + + objectAttributes = new LSA_OBJECT_ATTRIBUTES(); + objectAttributes.Length = 0; + objectAttributes.RootDirectory = IntPtr.Zero; + objectAttributes.Attributes = 0; + objectAttributes.SecurityDescriptor = IntPtr.Zero; + objectAttributes.SecurityQualityOfService = IntPtr.Zero; + + localsystem = new LSA_UNICODE_STRING(); + localsystem.Buffer = IntPtr.Zero; + localsystem.Length = 0; + localsystem.MaximumLength = 0; + + secretName = new LSA_UNICODE_STRING(); + secretName.Buffer = Marshal.StringToHGlobalUni(key); + secretName.Length = (UInt16)(key.Length * UnicodeEncoding.CharSize); + secretName.MaximumLength = (UInt16)((key.Length + 1) * UnicodeEncoding.CharSize); + } + + private IntPtr GetLsaPolicy(LSA_AccessPolicy access) + { + IntPtr LsaPolicyHandle; + + uint ntsResult = LsaOpenPolicy(ref this.localsystem, ref this.objectAttributes, (uint)access, out LsaPolicyHandle); + + uint winErrorCode = LsaNtStatusToWinError(ntsResult); + if (winErrorCode != 0) + { + throw new Exception("LsaOpenPolicy failed: " + winErrorCode); + } + + return LsaPolicyHandle; + } + + private static void ReleaseLsaPolicy(IntPtr LsaPolicyHandle) + { + uint ntsResult = LsaClose(LsaPolicyHandle); + uint winErrorCode = LsaNtStatusToWinError(ntsResult); + if (winErrorCode != 0) + { + throw new Exception("LsaClose failed: " + winErrorCode); + } + } + + public void SetSecret(string value) + { + LSA_UNICODE_STRING lusSecretData = new LSA_UNICODE_STRING(); + + if (value.Length > 0) + { + //Create data and key + lusSecretData.Buffer = Marshal.StringToHGlobalUni(value); + lusSecretData.Length = (UInt16)(value.Length * UnicodeEncoding.CharSize); + lusSecretData.MaximumLength = (UInt16)((value.Length + 1) * UnicodeEncoding.CharSize); + } + else + { + //Delete data and key + lusSecretData.Buffer = IntPtr.Zero; + lusSecretData.Length = 0; + lusSecretData.MaximumLength = 0; + } + + IntPtr LsaPolicyHandle = GetLsaPolicy(LSA_AccessPolicy.POLICY_CREATE_SECRET); + uint result = LsaStorePrivateData(LsaPolicyHandle, ref secretName, ref lusSecretData); + ReleaseLsaPolicy(LsaPolicyHandle); + + uint winErrorCode = LsaNtStatusToWinError(result); + if (winErrorCode != 0) + { + throw new Exception("StorePrivateData failed: " + winErrorCode); + } + } + } + } +"@ + +Function TestCredential { + param + ( + [PSCredential]$Credential + ) + try { + Start-Process -FilePath cmd.exe /c -Credential ($Credential) + } + Catch { + If ($Error[0].Exception.Message) { + $Error[0].Exception.Message + Throw + } + } + } + +function Set-AutoLogon { + [CmdletBinding(SupportsShouldProcess)] + param + ( + [PSCredential]$Credential + ) + Try { + if ($Credential.GetNetworkCredential().Domain) { + $DefaultDomainName = $Credential.GetNetworkCredential().Domain + } + elseif ((Get-WMIObject Win32_ComputerSystem).PartOfDomain) { + $DefaultDomainName = "." + } + else { + $DefaultDomainName = "" + } + + if ($PSCmdlet.ShouldProcess(('User "{0}\{1}"' -f $DefaultDomainName, $Credential.GetNetworkCredential().Username), "Set Auto logon")) { + Write-Verbose ('DomainName: {0} / UserName: {1}' -f $DefaultDomainName, $Credential.GetNetworkCredential().Username) + Set-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon' -Name "AutoAdminLogon" -Value 1 + Set-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon' -Name "DefaultDomainName" -Value "" + Set-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon' -Name "DefaultUserName" -Value $Credential.UserName + Remove-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon' -Name "AutoLogonCount" -ErrorAction SilentlyContinue + Remove-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon' -Name "DefaultPassword" -ErrorAction SilentlyContinue + $private:LsaUtil = New-Object ComputerSystem.LSAutil -ArgumentList "DefaultPassword" + $LsaUtil.SetSecret($Credential.GetNetworkCredential().Password) + "Auto Logon Configured" + Remove-Variable Credential + } + } + Catch { + $Error[0].Exception.Message + Throw + } +} + + +Function GetInstanceCredential { + Try { + $Credential = Get-Credential -Credential $null + Try { + TestCredential -Credential $Credential + } + Catch { + Remove-Variable Credential + #$Error[0].Exception.Message + "Retry?" + $Retry = Read-Host "(Y/N)" + Switch ($Retry){ + Y { + GetInstanceCredential + } + N { + Return + } + } + } + } + Catch { + if ($Credential) {Remove-Variable Credential} + "You pressed cancel, retry?" + $Cancel = Read-Host "(Y/N)" + Switch ($Cancel){ + Y { + GetInstanceCredential + } + N { + Return + } + } + } + if($credential) {Set-AutoLogon -Credential $Credential} + } + +Function PromptUserAutoLogon { +param ( +[switch]$DontPromptPasswordUpdateGPU +) +$CloudProvider = CloudProvider + If ($DontPromptPasswordUpdateGPU) { + } + ElseIf ($CloudProvider -eq "Paperspace") { + } + Else { + "Detected $CloudProvider" + Write-Host @" +Do you want this computer to log on to Windows automatically? +(Y): This is good when you want the cloud computer to boot straight to Parsec but is less secure as the computer will not be protected by a password at start up +(N): If you plan to log into Windows with RDP then connect via Parsec, or have been told you don't need to set this up +"@ -ForegroundColor Black -BackgroundColor Red + $ReadHost = Read-Host "(Y/N)" + Switch ($ReadHost) + { + Y { + GetInstanceCredential + } + N { + } + } + } + } + + + + + +#Modifies Local Group Policy to enable Shutdown scrips items +function add-gpo-modifications { + $querygpt = Get-content C:\Windows\System32\GroupPolicy\gpt.ini + $matchgpt = $querygpt -match '{42B5FAAE-6536-11D2-AE5A-0000F87571E3}{40B6664F-4972-11D1-A7CA-0000F87571E3}' + if ($matchgpt -contains "*0000F87571E3*" -eq $false) { + $gptstring = get-content C:\Windows\System32\GroupPolicy\gpt.ini + $gpoversion = $gptstring -match "Version" + $GPO = $gptstring -match "gPCMachineExtensionNames" + $add = '[{42B5FAAE-6536-11D2-AE5A-0000F87571E3}{40B6664F-4972-11D1-A7CA-0000F87571E3}]' + $replace = "$GPO" + "$add" + (Get-Content "C:\Windows\System32\GroupPolicy\gpt.ini").Replace("$GPO","$replace") | Set-Content "C:\Windows\System32\GroupPolicy\gpt.ini" + [int]$i = $gpoversion.trim("Version=") + [int]$n = $gpoversion.trim("Version=") + $n +=2 + (Get-Content C:\Windows\System32\GroupPolicy\gpt.ini) -replace "Version=$i", "Version=$n" | Set-Content C:\Windows\System32\GroupPolicy\gpt.ini + } + else{ + write-output "Not Required" + } + } + +#Adds Premade Group Policu Item if existing configuration doesn't exist +function addRegItems{ + ProgressWriter -Status "Adding Registry Items and Group Policy" -PercentComplete $PercentComplete + if (Test-Path ("C:\Windows\system32\GroupPolicy" + "\gpt.ini")) { + add-gpo-modifications + } + Else { + Move-Item -Path $path\ParsecTemp\PreInstall\gpt.ini -Destination C:\Windows\system32\GroupPolicy -Force | Out-Null + } + regedit /s $path\ParsecTemp\PreInstall\NetworkRestore.reg + regedit /s $path\ParsecTemp\PreInstall\ForceCloseShutDown.reg + New-PSDrive -PSProvider Registry -Name HKU -Root HKEY_USERS -ErrorAction SilentlyContinue | Out-Null + } + +function Test-RegistryValue { + # https://www.jonathanmedd.net/2014/02/testing-for-the-presence-of-a-registry-key-and-value.html + param ( + + [parameter(Mandatory=$true)] + [ValidateNotNullOrEmpty()]$Path, + + [parameter(Mandatory=$true)] + [ValidateNotNullOrEmpty()]$Value + ) + + try { + Get-ItemProperty -Path $Path | Select-Object -ExpandProperty $Value -ErrorAction Stop | Out-Null + return $true + } + catch { + return $false + } + +} + + +#Create ParsecTemp folder in C Drive +function create-directories { + ProgressWriter -Status "Creating Directories (C:\ParsecTemp)" -PercentComplete $PercentComplete + if((Test-Path -Path C:\ParsecTemp) -eq $true) {} Else {New-Item -Path C:\ParsecTemp -ItemType directory | Out-Null} + if((Test-Path -Path C:\ParsecTemp\Apps) -eq $true) {} Else {New-Item -Path C:\ParsecTemp\Apps -ItemType directory | Out-Null} + if((Test-Path -Path C:\ParsecTemp\DirectX) -eq $true) {} Else {New-Item -Path C:\ParsecTemp\DirectX -ItemType directory | Out-Null} + if((Test-Path -Path C:\ParsecTemp\Drivers) -eq $true) {} Else {New-Item -Path C:\ParsecTemp\Drivers -ItemType Directory | Out-Null} + # if((Test-Path -Path C:\ParsecTemp\Devcon) -eq $true) {} Else {New-Item -Path C:\ParsecTemp\Devcon -ItemType Directory | Out-Null} + } + + #Create ParsecTemp folder in C Drive +function unzip-preinstall { + ProgressWriter -Status "Creating Directories (C:\ParsecTemp)" -PercentComplete $PercentComplete + 7z x ".\PreInstall.zip" "-oC:\ParsecTemp\PreInstall" -bd + } + +#disable IE security +function disable-iesecurity { + ProgressWriter -Status "Disabling Internet Explorer security to enable web browsing" -PercentComplete $PercentComplete + Set-Itemproperty "HKLM:\SOFTWARE\Microsoft\Active Setup\Installed Components\{A509B1A7-37EF-4b3f-8CFC-4F3A74704073}" -name IsInstalled -value 0 -force | Out-Null + Set-ItemProperty "HKLM:\SOFTWARE\Microsoft\Active Setup\Installed Components\{A509B1A8-37EF-4b3f-8CFC-4F3A74704073}" -Name IsInstalled -Value 0 -Force | Out-Null + # Stop-Process -Name Explorer -Force + } + +#download-files-S3 +function download-resources { + $ProgressPreference = 'SilentlyContinue' + + #ProgressWriter -Status "Downloading DirectX June 2010 Redist" -PercentComplete $PercentComplete + #Invoke-WebRequest -Uri "https://download.microsoft.com/download/8/4/A/84A35BF1-DAFE-4AE8-82AF-AD2AE20B6B14/directx_Jun2010_redist.exe" -OutFile "C:\ParsecTemp\Apps\directx_Jun2010_redist.exe" + # ProgressWriter -Status "Downloading Devcon" -PercentComplete $PercentComplete + # Invoke-WebRequest -Uri "https://s3.amazonaws.com/parsec-files-ami-setup/Devcon/devcon.exe" -OutFile "C:\ParsecTemp\Devcon\devcon.exe" + ProgressWriter -Status "Downloading Parsec" -PercentComplete $PercentComplete + Invoke-WebRequest -Uri "https://builds.parsecgaming.com/package/parsec-windows.exe" -OutFile "C:\ParsecTemp\Apps\parsec-windows.exe" + ProgressWriter -Status "Downloading GPU Updater" -PercentComplete $PercentComplete + Invoke-WebRequest -Uri "https://s3.amazonaws.com/parseccloud/image/parsec+desktop.png" -OutFile "C:\ParsecTemp\parsec+desktop.png" + Invoke-WebRequest -Uri "https://s3.amazonaws.com/parseccloud/image/white_ico_agc_icon.ico" -OutFile "C:\ParsecTemp\white_ico_agc_icon.ico" + #Invoke-WebRequest -Uri "https://raw.githubusercontent.com/parsec-cloud/Cloud-GPU-Updater/master/GPUUpdaterTool.ps1" -OutFile "$env:ProgramData\ParsecLoader\GPUUpdaterTool.ps1" + #ProgressWriter -Status "Downloading Google Chrome" -PercentComplete $PercentComplete + #Invoke-WebRequest -Uri "https://dl.google.com/tag/s/dl/chrome/install/googlechromestandaloneenterprise64.msi" -OutFile "C:\ParsecTemp\Apps\googlechromestandaloneenterprise64.msi" + } + +#install-base-files-silently +function install-windows-features { + ProgressWriter -Status "Installing Chrome" -PercentComplete $PercentComplete + start-process -filepath "C:\Windows\System32\msiexec.exe" -ArgumentList '/qn /i "C:\ParsecTemp\Apps\googlechromestandaloneenterprise64.msi"' -Wait + ProgressWriter -Status "Installing DirectX June 2010 Redist" -PercentComplete $PercentComplete + Start-Process -FilePath "C:\ParsecTemp\Apps\directx_jun2010_redist.exe" -ArgumentList '/T:C:\ParsecTemp\DirectX /Q'-wait + Start-Process -FilePath "C:\ParsecTemp\DirectX\DXSETUP.EXE" -ArgumentList '/silent' -wait + ProgressWriter -Status "Installing Direct Play" -PercentComplete $PercentComplete + Install-WindowsFeature Direct-Play | Out-Null + ProgressWriter -Status "Installing .net 3.5" -PercentComplete $PercentComplete + Install-WindowsFeature Net-Framework-Core | Out-Null + ProgressWriter -Status "Cleaning up" -PercentComplete $PercentComplete + Remove-Item -Path C:\ParsecTemp\DirectX -force -Recurse + } + +Function TeamMachineSetupScheduledTask { +$XML = @" + + + + Attempts to read instance userdata and set up as Team Machine at startup + \Setup Team Machine + + + + true + + + + + $(([System.Security.Principal.WindowsIdentity]::GetCurrent()).User.Value) + S4U + HighestAvailable + + + + IgnoreNew + true + true + true + false + false + + true + false + + true + true + false + false + false + PT72H + 7 + + + + C:\WINDOWS\system32\WindowsPowerShell\v1.0\powershell.exe + -file %programdata%\ParsecLoader\TeamMachineSetup.ps1 + + + +"@ + + try { + Get-ScheduledTask -TaskName "Setup Team Machine" -ErrorAction Stop | Out-Null + Unregister-ScheduledTask -TaskName "Setup Team Machine" -Confirm:$false + } + catch {} + $action = New-ScheduledTaskAction -Execute 'C:\WINDOWS\system32\WindowsPowerShell\v1.0\powershell.exe' -Argument '-file %programdata%\ParsecLoader\TeamMachineSetup.ps1' + $trigger = New-ScheduledTaskTrigger -AtStartup + Register-ScheduledTask -XML $XML -TaskName "Setup Team Machine" | Out-Null + } + +#set update policy +function set-update-policy { + ProgressWriter -Status "Disabling Windows Update" -PercentComplete $PercentComplete + if((Test-RegistryValue -path 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate' -value 'DoNotConnectToWindowsUpdateInternetLocations') -eq $true) {Set-itemproperty -path HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate -Name "DoNotConnectToWindowsUpdateInternetLocations" -Value "1" | Out-Null} else {new-itemproperty -path HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate -Name "DoNotConnectToWindowsUpdateInternetLocations" -Value "1" | Out-Null} + if((Test-RegistryValue -path 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate' -value 'UpdateServiceURLAlternative') -eq $true) {Set-itemproperty -path HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate -Name "UpdateServiceURLAlternative" -Value "http://intentionally.disabled" | Out-Null} else {new-itemproperty -path HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate -Name "UpdateServiceURLAlternative" -Value "http://intentionally.disabled" | Out-Null} + if((Test-RegistryValue -path 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate' -value 'WUServer') -eq $true) {Set-itemproperty -path HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate -Name "WUServer" -Value "http://intentionally.disabled" | Out-Null} else {new-itemproperty -path HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate -Name "WUServer" -Value "http://intentionally.disabled" | Out-Null} + if((Test-RegistryValue -path 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate' -value 'WUSatusServer') -eq $true) {Set-itemproperty -path HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate -Name "WUSatusServer" -Value "http://intentionally.disabled" | Out-Null} else {new-itemproperty -path HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate -Name "WUSatusServer" -Value "http://intentionally.disabled" | Out-Null} + Set-ItemProperty -Path HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU -Name "AUOptions" -Value 1 | Out-Null + if((Test-RegistryValue -path 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU' -value 'UseWUServer') -eq $true) {Set-itemproperty -path HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU -Name "UseWUServer" -Value 1 | Out-Null} else {new-ItemProperty -Path HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU -Name "UseWUServer" -Value 1 | Out-Null} + } + +#set automatic time and timezone +function set-time { + ProgressWriter -Status "Setting computer time to automatic" -PercentComplete $PercentComplete + Set-ItemProperty -path HKLM:\SYSTEM\CurrentControlSet\Services\W32Time\Parameters -Name Type -Value NTP | Out-Null + Set-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Services\tzautoupdate -Name Start -Value 00000003 | Out-Null + } + +#disable new network window +function disable-network-window { + ProgressWriter -Status "Disabling New Network Window" -PercentComplete $PercentComplete + if((Test-RegistryValue -path HKLM:\SYSTEM\CurrentControlSet\Control\Network -Value NewNetworkWindowOff)-eq $true) {} Else {new-itemproperty -path HKLM:\SYSTEM\CurrentControlSet\Control\Network -name "NewNetworkWindowOff" | Out-Null} + } + +#Enable Pointer Precision +function enhance-pointer-precision { + ProgressWriter -Status "Enabling enchanced pointer precision" -PercentComplete $PercentComplete + Set-Itemproperty -Path 'HKCU:\Control Panel\Mouse' -Name MouseSpeed -Value 1 | Out-Null + } + +#enable Mouse Keys +function enable-mousekeys { + ProgressWriter -Status "Enabling mouse keys to assist with mouse cursor" -PercentComplete $PercentComplete + set-Itemproperty -Path 'HKCU:\Control Panel\Accessibility\MouseKeys' -Name Flags -Value 63 | Out-Null + } + +#disable shutdown start menu +function remove-shutdown { + Write-Output "Disabling Shutdown Option in Start Menu" + New-ItemProperty -Path HKLM:\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer -Name NoClose -Value 1 | Out-Null + } + +#Sets all applications to force close on shutdown +function force-close-apps { + ProgressWriter -Status "Setting Windows not to stop shutdown if there are unsaved apps" -PercentComplete $PercentComplete + if (((Get-Item -Path "HKCU:\Control Panel\Desktop").GetValue("AutoEndTasks") -ne $null) -eq $true) { + Set-ItemProperty -path "HKCU:\Control Panel\Desktop" -Name "AutoEndTasks" -Value "1" + } + Else { + New-ItemProperty -path "HKCU:\Control Panel\Desktop" -Name "AutoEndTasks" -Value "1" + } + } + +#show hidden items +function show-hidden-items { + ProgressWriter -Status "Showing hidden files in Windows Explorer" -PercentComplete $PercentComplete + set-itemproperty -path "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced" -Name Hidden -Value 1 | Out-Null + } + +#show file extensions +function show-file-extensions { + ProgressWriter -Status "Showing file extensions in Windows Explorer" -PercentComplete $PercentComplete + Set-itemproperty -path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced" -name HideFileExt -Value 0 | Out-Null + } + +#disable logout start menu +function disable-logout { + ProgressWriter -Status "Disabling log out button on start menu" -PercentComplete $PercentComplete + if((Test-RegistryValue -Path HKLM:\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer -Value StartMenuLogOff )-eq $true) {Set-ItemProperty -Path HKLM:\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer -Name StartMenuLogOff -Value 1 | Out-Null} Else {New-ItemProperty -Path HKLM:\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer -Name StartMenuLogOff -Value 1 | Out-Null} + } + +#disable lock start menu +function disable-lock { + ProgressWriter -Status "Disabling option to lock your Windows user profile" -PercentComplete $PercentComplete + if((Test-Path -Path HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System) -eq $true) {} Else {New-Item -Path HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies -Name Software | Out-Null} + if((Test-RegistryValue -Path HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System -Value DisableLockWorkstation) -eq $true) {Set-ItemProperty -Path HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System -Name DisableLockWorkstation -Value 1 | Out-Null } Else {New-ItemProperty -Path HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System -Name DisableLockWorkstation -Value 1 | Out-Null} + } + +#set wallpaper +function set-wallpaper { + ProgressWriter -Status "Setting the Parsec logo ass the computer wallpaper" -PercentComplete $PercentComplete + New-Item -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Policies" -Name "System" | Out-Null + New-ItemProperty -Path HKCU:\Software\Microsoft\Windows\CurrentVersion\Policies\System -Name Wallpaper -PropertyType String -value "C:\ParsecTemp\parsec+desktop.png" | Out-Null + New-ItemProperty -Path HKCU:\Software\Microsoft\Windows\CurrentVersion\Policies\System -Name WallpaperStyle -PropertyType String -value 2 | Out-Null + #Stop-Process -ProcessName explorer + } + +#disable recent start menu items +function disable-recent-start-menu { + New-Item -path HKLM:\SOFTWARE\Policies\Microsoft\Windows -name Explorer + New-ItemProperty -Path HKLM:\SOFTWARE\Policies\Microsoft\Windows\Explorer -PropertyType DWORD -Name HideRecentlyAddedApps -Value 1 + } + +#createshortcut +function Create-AutoShutdown-Shortcut{ + ProgressWriter -Status "Creating auto shutdown shortcut" -PercentComplete $PercentComplete + $Shell = New-Object -ComObject ("WScript.Shell") + $ShortCut = $Shell.CreateShortcut("$env:PUBLIC\Desktop\Setup Auto Shutdown.lnk") + $ShortCut.TargetPath="powershell.exe" + $ShortCut.Arguments='-ExecutionPolicy Bypass -File "C:\ProgramData\ParsecLoader\CreateAutomaticShutdownScheduledTask.ps1"' + $ShortCut.WorkingDirectory = "$env:ProgramData\ParsecLoader"; + $ShortCut.WindowStyle = 0; + $ShortCut.Description = "ClearProxy shortcut"; + $ShortCut.Save() + } + +#createshortcut +function Create-One-Hour-Warning-Shortcut{ + ProgressWriter -Status "Creating one hour warning shortcut" -PercentComplete $PercentComplete + $Shell = New-Object -ComObject ("WScript.Shell") + $ShortCut = $Shell.CreateShortcut("$env:PUBLIC\Desktop\Setup One Hour Warning.lnk") + $ShortCut.TargetPath="powershell.exe" + $ShortCut.Arguments='-ExecutionPolicy Bypass -File "C:\ProgramData\ParsecLoader\CreateOneHourWarningScheduledTask.ps1"' + $ShortCut.WorkingDirectory = "$env:ProgramData\ParsecLoader"; + $ShortCut.WindowStyle = 0; + $ShortCut.Description = "OneHourWarning shortcut"; + $ShortCut.Save() + } + +#create shortcut for electron app +#function create-shortcut-app { +# Copy-Item -Path $path\ParsecTemp\PostInstall\Parsec.lnk -Destination $path +# } + +#Disables Server Manager opening on Startup +function disable-server-manager { + ProgressWriter -Status "Disabling Windows Server Manager from starting at startup" -PercentComplete $PercentComplete + Get-ScheduledTask -TaskName ServerManager | Disable-ScheduledTask | Out-Null + } + +#AWS Clean up Desktop Items +function clean-aws { + remove-item -path "$path\EC2 Feedback.Website" + Remove-Item -Path "$path\EC2 Microsoft Windows Guide.website" + } + +#Move extracts Razer Surround Files into correct location +Function ExtractRazerAudio { + cmd.exe /c '"C:\Program Files\7-Zip\7z.exe" x C:\ParsecTemp\Apps\razer-surround-driver.exe -oC:\ParsecTemp\Apps\razer-surround-driver -y' | Out-Null + } + +#modifys the installer manifest to run without interraction +Function ModidifyManifest { + $InstallerManifest = 'C:\ParsecTemp\Apps\razer-surround-driver\$TEMP\RazerSurroundInstaller\InstallerManifest.xml' + $regex = '(?<=)[^<]*' + (Get-Content $InstallerManifest) -replace $regex, 'true' | Set-Content $InstallerManifest -Encoding UTF8 + } + + #Audio Driver Install +function AudioInstall { + Invoke-WebRequest -Uri "http://rzr.to/surround-pc-download" -OutFile "C:\ParsecTemp\Apps\razer-surround-driver.exe" + ExtractRazerAudio + ModidifyManifest + $OriginalLocation = Get-Location + Set-Location -Path 'C:\ParsecTemp\Apps\razer-surround-driver\$TEMP\RazerSurroundInstaller\' + Start-Process RzUpdateManager.exe + Set-Location $OriginalLocation + Set-Service -Name audiosrv -StartupType Automatic + } + +#Creates shortcut for the GPU Updater tool +function gpu-update-shortcut { + Invoke-WebRequest -Uri "https://raw.githubusercontent.com/parsec-cloud/Cloud-GPU-Updater/master/GPUUpdaterTool.ps1" -OutFile "$env:ProgramData\ParsecLoader\GPUUpdaterTool.ps1" + Unblock-File -Path "$env:ProgramData\ParsecLoader\GPUUpdaterTool.ps1" + ProgressWriter -Status "Creating GPU Updater icon on Desktop" -PercentComplete $PercentComplete + $Shell = New-Object -ComObject ("WScript.Shell") + $ShortCut = $Shell.CreateShortcut("$path\GPU Updater.lnk") + $ShortCut.TargetPath="powershell.exe" + $ShortCut.Arguments='-ExecutionPolicy Bypass -File "C:\ProgramData\ParsecLoader\GPUUpdaterTool.ps1"' + $ShortCut.WorkingDirectory = "$env:ProgramData\ParsecLoader"; + $ShortCut.IconLocation = "$env:ProgramData\ParsecLoader\GPU-Update.ico, 0"; + $ShortCut.WindowStyle = 0; + $ShortCut.Description = "GPU Updater shortcut"; + $ShortCut.Save() + } + +#Provider specific driver install and setup +Function provider-specific { + ProgressWriter -Status "Installing Audio Driver if required and removing system information from appearing on Google Cloud Desktops" -PercentComplete $PercentComplete + #Device ID Query + $gputype = get-wmiobject -query "select DeviceID from Win32_PNPEntity Where (deviceid Like '%PCI\\VEN_10DE%') and (PNPClass = 'Display' or Name = '3D Video Controller')" | Select-Object DeviceID -ExpandProperty DeviceID + if ($gputype -eq $null) { + } + Else { + if($gputype.substring(13,8) -eq "DEV_13F2") { + #AWS G3.4xLarge M60 + #AudioInstall + } + ElseIF($gputype.Substring(13,8) -eq "DEV_118A"){ + #AWS G2.2xLarge K520 + #AudioInstall + } + ElseIF($gputype.Substring(13,8) -eq "DEV_1BB1") { + #Paperspace P4000 + } + Elseif($gputype.Substring(13,8) -eq "DEV_1BB0") { + #Paperspace P5000 + } + Elseif($gputype.substring(13,8) -eq "DEV_15F8") { + #Tesla P100 + if((Test-Path "C:\Program Files\Google\Compute Engine\tools\BGInfo.exe") -eq $true) {remove-item -path "C:\Program Files\Google\Compute Engine\tools\BGInfo.exe"} Else {} + if((Test-Path "C:\ProgramData\Microsoft\Windows\Start Menu\Programs\StartUp\BGinfo.lnk") -eq $true) {Remove-Item -path "C:\ProgramData\Microsoft\Windows\Start Menu\Programs\StartUp\BGinfo.lnk"} Else {} + #AudioInstall + } + Elseif($gputype.substring(13,8) -eq "DEV_1BB3") { + #Tesla P4 + if((Test-Path "C:\Program Files\Google\Compute Engine\tools\BGInfo.exe") -eq $true) {remove-item -path "C:\Program Files\Google\Compute Engine\tools\BGInfo.exe"} Else {} + if((Test-Path "C:\ProgramData\Microsoft\Windows\Start Menu\Programs\StartUp\BGinfo.lnk") -eq $true) {Remove-Item -path "C:\ProgramData\Microsoft\Windows\Start Menu\Programs\StartUp\BGinfo.lnk"} Else {} + #AudioInstall + } + Elseif($gputype.substring(13,8) -eq "DEV_1EB8") { + #Tesla T4 + if((Test-Path "C:\Program Files\Google\Compute Engine\tools\BGInfo.exe") -eq $true) {remove-item -path "C:\Program Files\Google\Compute Engine\tools\BGInfo.exe"} Else {} + if((Test-Path "C:\ProgramData\Microsoft\Windows\Start Menu\Programs\StartUp\BGinfo.lnk") -eq $true) {Remove-Item -path "C:\ProgramData\Microsoft\Windows\Start Menu\Programs\StartUp\BGinfo.lnk"} Else {} + #AudioInstall + } + Elseif($gputype.substring(13,8) -eq "DEV_1430") { + #Quadro M2000 + #AudioInstall + } + Else { + } + } + } + +#7Zip is required to extract the Parsec-Windows.exe File +function Install7Zip { + $url = Invoke-WebRequest -Uri https://www.7-zip.org/download.html + Invoke-WebRequest "https://www.7-zip.org/$($($($url.Links | Where-Object outertext -Like "Download")[1]).OuterHTML.split('"')[1])" -OutFile "C:\ParsecTemp\Apps\7zip.exe" + Start-Process C:\ParsecTemp\Apps\7zip.exe -ArgumentList '/S /D="C:\Program Files\7-Zip"' -Wait + } + +#Move Parsec Files into correct location +#Function ExtractInstallFiles { +# cmd.exe /c '"C:\Program Files\7-Zip\7z.exe" x C:\ParsecTemp\Apps\parsec-windows.exe -oC:\ParsecTemp\Apps\Parsec-Windows -y' | Out-Null +# if((Test-Path -Path 'C:\Program Files\Parsec')-eq $true) {} Else {New-Item -Path 'C:\Program Files\Parsec' -ItemType Directory | Out-Null} +# if((Test-Path -Path "C:\Program Files\Parsec\skel") -eq $true) {} Else {Move-Item -Path C:\ParsecTemp\Apps\Parsec-Windows\skel -Destination 'C:\Program Files\Parsec' | Out-Null} +# if((Test-Path -Path "C:\Program Files\Parsec\vigem") -eq $true) {} Else {Move-Item -Path C:\ParsecTemp\Apps\Parsec-Windows\vigem -Destination 'C:\Program Files\Parsec' | Out-Null} +# if((Test-Path -Path "C:\Program Files\Parsec\wscripts") -eq $true) {} Else {Move-Item -Path C:\ParsecTemp\Apps\Parsec-Windows\wscripts -Destination 'C:\Program Files\Parsec' | Out-Null} +# if((Test-Path -Path "C:\Program Files\Parsec\parsecd.exe") -eq $true) {} Else {Move-Item -Path C:\ParsecTemp\Apps\Parsec-Windows\parsecd.exe -Destination 'C:\Program Files\Parsec' | Out-Null} +# if((Test-Path -Path "C:\Program Files\Parsec\pservice.exe") -eq $true) {} Else {Move-Item -Path C:\ParsecTemp\Apps\Parsec-Windows\pservice.exe -Destination 'C:\Program Files\Parsec' | Out-Null} +# Start-Sleep 1 +# } + +#Checks for Server 2019 and asks user to install Windows Xbox Accessories in order to let their controller work +Function Server2019Controller { + ProgressWriter -Status "Adding Xbox 360 Controller driver to Windows Server 2019" -PercentComplete $PercentComplete + if ((gwmi win32_operatingsystem | % caption) -like '*Windows Server 2019*') { + Invoke-WebRequest -Uri "http://www.download.windowsupdate.com/msdownload/update/v3-19990518/cabpool/2060_8edb3031ef495d4e4247e51dcb11bef24d2c4da7.cab" -OutFile "C:\ParsecTemp\Drivers\Xbox360_64Eng.cab" + if((Test-Path -Path C:\ParsecTemp\Drivers\Xbox360_64Eng) -eq $true) {} Else {New-Item -Path C:\ParsecTemp\Drivers\Xbox360_64Eng -ItemType directory | Out-Null} + cmd.exe /c "C:\Windows\System32\expand.exe C:\ParsecTemp\Drivers\Xbox360_64Eng.cab -F:* C:\ParsecTemp\Drivers\Xbox360_64Eng" | Out-Null + cmd.exe /c '"C:\Program Files\Parsec\vigem\10\x64\devcon.exe" dp_add "C:\ParsecTemp\Drivers\Xbox360_64Eng\xusb21.inf"' | Out-Null + } + } + +#Function InstallViGEmBus { + #Required for Controller Support. + #$Vigem = @{} + #$Vigem.DriverFile = "C:\Program Files\Parsec\Vigem\ViGEmBus.cat"; + #$Vigem.CertName = 'C:\Program Files\Parsec\Vigem\Wohlfeil_IT_e_U_.cer'; + #$Vigem.ExportType = [System.Security.Cryptography.X509Certificates.X509ContentType]::Cert; + #$Vigem.Cert = (Get-AuthenticodeSignature -filepath $vigem.DriverFile).SignerCertificate; + #$Vigem.CertInstalled = if ((Get-ChildItem -Path Cert:\CurrentUser\TrustedPublisher | Where-Object Subject -Like "*CN=Wohlfeil.IT e.U., O=Wohlfeil.IT e.U.*" ) -ne $null) {$True} + #Else {$false} + #if ($vigem.CertInstalled -eq $true) { + #cmd.exe /c '"C:\Program Files\Parsec\vigem\10\x64\devcon.exe" install "C:\Program Files\Parsec\vigem\10\ViGEmBus.inf" Nefarius\ViGEmBus\Gen1' | Out-Null + #} + #Else {[System.IO.File]::WriteAllBytes($Vigem.CertName, $Vigem.Cert.Export($Vigem.ExportType)); + #Import-Certificate -CertStoreLocation Cert:\LocalMachine\TrustedPublisher -FilePath 'C:\Program Files\Parsec\Vigem\Wohlfeil_IT_e_U_.cer' | Out-Null + #Start-Sleep 5 + #cmd.exe /c '"C:\Program Files\Parsec\vigem\devcon.exe" install "C:\Program Files\Parsec\vigem\ViGEmBus.inf" Root\ViGEmBus' | Out-Null + #} + #} + +#Creates Parsec Firewall Rule in Windows Firewall +#Function CreateFireWallRule { +# New-NetFirewallRule -DisplayName "Parsec" -Direction Inbound -Program "C:\Program Files\Parsec\Parsecd.exe" -Profile Private,Public -Action Allow -Enabled True | Out-Null +# } + +#Creates Parsec Service +#Function CreateParsecService { +# cmd.exe /c 'sc.exe Create "Parsec" binPath= "\"C:\Program Files\Parsec\pservice.exe\"" start= "auto"' | Out-Null +# sc.exe Start 'Parsec' | Out-Null +# } + + +Function InstallParsec { + Start-Process "C:\ParsecTemp\Apps\parsec-windows.exe" -ArgumentList "/silent", "/shared" -wait +# ExtractInstallFiles +# InstallViGEmBus +# CreateFireWallRule +# CreateParsecService +# create-shortcut-app + } + +#Apps that require human intervention +function Install-Gaming-Apps { + ProgressWriter -Status "Installing Parsec, ViGEm https://github.com/ViGEm/ViGEmBus and 7Zip" -PercentComplete $PercentComplete + #Install7Zip + InstallParsec + #if((Test-RegistryValue -path HKCU:\Software\Microsoft\Windows\CurrentVersion\Run -value "Parsec.App.0") -eq $true) {Set-ItemProperty -path HKCU:\Software\Microsoft\Windows\CurrentVersion\Run -Name "Parsec.App.0" -Value "C:\Program Files\Parsec\parsecd.exe" | Out-Null} Else {New-ItemProperty -path HKCU:\Software\Microsoft\Windows\CurrentVersion\Run -Name "Parsec.App.0" -Value "C:\Program Files\Parsec\parsecd.exe" | Out-Null} + #Start-Process -FilePath "C:\Program Files\Parsec\parsecd.exe" + Start-Sleep -s 1 + } + +#Disable Devices +function disable-devices { + ProgressWriter -Status "Disabling Microsoft Basic Display Adapter, Generic Non PNP Monitor and other devices" -PercentComplete $PercentComplete + Start-Process -FilePath "C:\Program Files\Parsec\vigem\10\x64\devcon.exe" -ArgumentList '/r disable "HDAUDIO\FUNC_01&VEN_10DE&DEV_0083&SUBSYS_10DE11A3*"' + Get-PnpDevice | where {$_.friendlyname -like "Generic Non-PNP Monitor" -and $_.status -eq "OK"} | Disable-PnpDevice -confirm:$false + Get-PnpDevice | where {$_.friendlyname -like "Microsoft Basic Display Adapter" -and $_.status -eq "OK"} | Disable-PnpDevice -confirm:$false + Get-PnpDevice | where {$_.friendlyname -like "Google Graphics Array (GGA)" -and $_.status -eq "OK"} | Disable-PnpDevice -confirm:$false + Start-Process -FilePath "C:\Program Files\Parsec\vigem\10\x64\devcon.exe" -ArgumentList '/r disable "PCI\VEN_1013&DEV_00B8*"' + Start-Process -FilePath "C:\Program Files\Parsec\vigem\10\x64\devcon.exe" -ArgumentList '/r disable "PCI\VEN_1D0F&DEV_1111*"' + Start-Process -FilePath "C:\Program Files\Parsec\vigem\10\x64\devcon.exe" -ArgumentList '/r disable "PCI\VEN_1AE0&DEV_A002*"' + } + +#Cleanup +function clean-up { + ProgressWriter -Status "Deleting temporary files from C:\ParsecTemp" -PercentComplete $PercentComplete + Remove-Item -Path C:\ParsecTemp\Drivers -force -Recurse + Remove-Item -Path $path\ParsecTemp -force -Recurse + } + +#cleanup recent files +function clean-up-recent { + ProgressWriter -Status "Delete recently accessed files list from Windows Explorer" -PercentComplete $PercentComplete + remove-item "$env:AppData\Microsoft\Windows\Recent\*" -Recurse -Force | Out-Null + } + +#stopping all parsec services +function stop-parsec { + ProgressWriter -Status "Stopping all Parsec Services and restarting" -PercentComplete $PercentComplete + Stop-Process -Name pservice -Force + Stop-Process -Name parsecd -Force + + Start-Service -Name Parsec +} + +function register-team-computer { + ProgressWriter -Status "Registering Team Computer" -PercentComplete $PercentComplete + $Output = Start-Process powershell.exe -argument "-file $env:ProgramData\ParsecLoader\TeamMachineSetup.ps1" -PassThru -Wait + if ($Output.ExitCode -ne 0) { + ProgressWriter -Status "Problem registering" -PercentComplete $PercentComplete + $errMsg = "There was a problem registering your team computer with the team ID and key provided." + Add-Content 'C:\Users\Public\Desktop\INSTALLED_SOFTWARE.txt' "ERROR: $($errMsg)" + throw $errMsg + } +} + +#Start GPU Update Tool +Function StartGPUUpdate { + param( + [switch]$DontPromptPasswordUpdateGPU + ) + if ($DontPromptPasswordUpdateGPU) { + } + Else { + start-process powershell.exe -verb RunAS -argument "-file $env:ProgramData\ParsecLoader\GPUUpdaterTool.ps1" + } + } +Write-Host -foregroundcolor red " + ((////// + #######////// + ##########(/////. + #############(/////, + #################/////* + #######/############////. + #######/// ##########//// + #######/// /#######/// + #######/// #######/// + #######/// #######/// + #######//// #######/// + ########////// #######/// + ###########////#######/// + ####################/// + ################/// + *#############/// + ##########/// + ######(* + + + + ~Parsec Self Hosted Cloud Setup Script~ + + This script sets up your cloud computer + with a bunch of settings and drivers + to make your life easier. + + It's provided with no warranty, + so use it at your own risk. + + Check out the README.md for more + information. + + This tool supports: + + OS: + Server 2016 + Server 2019 + + CLOUD SKU: + AWS G3.4xLarge (Tesla M60) + AWS G2.2xLarge (GRID K520) + AWS G4dn.xLarge (Tesla T4 with vGaming driver) + Azure NV6 (Tesla M60) + Paperspace P4000 (Quadro P4000) + Paperspace P5000 (Quadro P5000) + Google P100 VW (Tesla P100 Virtual Workstation) + Google P4 VW (Tesla P4 Virtual Workstation) + Google T4 VW (Tesla T4 Virtual Workstation) + +" +#PromptUserAutoLogon -DontPromptPasswordUpdateGPU:$DontPromptPasswordUpdateGPU +$ScripttaskList = @( +"unzip-preinstall"; +"setupEnvironment"; +"addRegItems"; +"create-directories"; +# "disable-iesecurity"; +"download-resources"; +#"install-windows-features"; +"force-close-apps"; +"disable-network-window"; +"disable-logout"; +"disable-lock"; +#"show-hidden-items"; +#"show-file-extensions"; +#"enhance-pointer-precision"; +#"enable-mousekeys"; +"set-time"; +#"set-wallpaper"; +#"Create-AutoShutdown-Shortcut"; +#"Create-One-Hour-Warning-Shortcut"; +#"disable-server-manager"; +"Install-Gaming-Apps"; +"Server2019Controller"; +#"gpu-update-shortcut"; +"disable-devices"; +#"clean-up"; +#"clean-up-recent"; +"provider-specific"; +# "TeamMachineSetupScheduledTask"; +"register-team-computer"; +"stop-parsec" +) + +foreach ($func in $ScripttaskList) { + $PercentComplete =$($ScriptTaskList.IndexOf($func) / $ScripttaskList.Count * 100) + & $func $PercentComplete + } + +#StartGPUUpdate -DontPromptPasswordUpdateGPU:$DontPromptPasswordUpdateGPU +ProgressWriter -status "Done" -percentcomplete 100 +Write-Host "1. Open Parsec and sign in" -ForegroundColor black -BackgroundColor Green +Write-Host "2. Use GPU Updater to update your GPU Drivers!" -ForegroundColor black -BackgroundColor Green +Write-Host "You don't need to sign into Razer Synapse, the login box will stop appearing after a couple of reboots" -ForegroundColor black -BackgroundColor Green +Write-Host "You may want to change your Windows password to something simpler if the password your cloud provider gave you is super long" -ForegroundColor black -BackgroundColor Green +Write-host "DONE!" -ForegroundColor black -BackgroundColor Green +#pause From c28102b7d2fe1827efbe57f918faab5ae0681913 Mon Sep 17 00:00:00 2001 From: Daniel Ciborowski Date: Fri, 22 Apr 2022 21:23:00 -0400 Subject: [PATCH 46/71] Update remoteAccessExtension.bicep --- .../remoteAccessExtension.bicep | 65 +++++++++++++++---- 1 file changed, 51 insertions(+), 14 deletions(-) diff --git a/application-workloads/azure-gamedev/gamedev-vm/nestedtemplates/remoteAccessExtension.bicep b/application-workloads/azure-gamedev/gamedev-vm/nestedtemplates/remoteAccessExtension.bicep index f2ab0a5970cd..632d22dfd696 100644 --- a/application-workloads/azure-gamedev/gamedev-vm/nestedtemplates/remoteAccessExtension.bicep +++ b/application-workloads/azure-gamedev/gamedev-vm/nestedtemplates/remoteAccessExtension.bicep @@ -1,25 +1,62 @@ -param _artifactsLocation string = deployment().properties.templateLink.uri +@description('Deployment Location') +param location string + +@description('The base URI where artifacts required by this template are located including a trailing \'/\'') +param _artifactsLocation string = deployment().properties.templateLink.uri + +@description('The sasToken required to access _artifactsLocation.') @secure() param _artifactsLocationSasToken string = '' -param cmdGDKInstall string -param cmdTeradiciRegistration string -param virtualMachineName string -param remoteAccessTechnology string -param location string -param fileShareStorageAccount string +@description('Toggle to Install GDK') +param cmdGDKInstall string + +@description('Toggle to Register Teradici') +param cmdTeradiciRegistration string + +@description('Name of Game Developer Virtual Machine') +param virtualMachineName string + +@description('Remote Access technology') +@allowed([ + 'RDP' + 'Teradici' + 'Parsec' +]) +param remoteAccessTechnology string + +@description('Name of Storage Account to Link') +param fileShareStorageAccount string + +@description('Key of Storage Account to Link') @secure() param fileShareStorageAccountKey string -param fileShareName string -param p4Port string -param p4Username string + +@description('Name of File Share') +param fileShareName string + +@description('Port to use for p4') +param p4Port string + +@description('Username for p4') +param p4Username string + + +@description('Password for p4') @secure() -param p4Password string -param p4Workspace string -param p4Stream string -param p4ClientViews string +param p4Password string + +@description('p4 Workspace') +param p4Workspace string + +@description('Stream for p4') +param p4Stream string + +@description('Client View for p4') +param p4ClientViews string var mountFileShareParams = '-storageAccount \'${fileShareStorageAccount}\' -storageAccountKey \'${fileShareStorageAccountKey}\' -fileShareName \'${fileShareName}\'' + var p4Params = '-p4Port \'${p4Port}\' -p4Username \'${p4Username}\' -p4Password \'${p4Password}\' -p4Workspace \'${p4Workspace}\' -p4Stream \'${p4Stream}\' -p4ClientViews \'${p4ClientViews}\'' var remoteAccessExtension = { From d7581b81d8871c395610a72951f2b473e7334306 Mon Sep 17 00:00:00 2001 From: Daniel Ciborowski Date: Fri, 22 Apr 2022 21:33:01 -0400 Subject: [PATCH 47/71] update arm from bicep --- .../azure-gamedev/gamedev-vm/azuredeploy.json | 95 +++++++++++++++---- 1 file changed, 74 insertions(+), 21 deletions(-) diff --git a/application-workloads/azure-gamedev/gamedev-vm/azuredeploy.json b/application-workloads/azure-gamedev/gamedev-vm/azuredeploy.json index 8c1feb960b7c..3f12b7cc7a3e 100644 --- a/application-workloads/azure-gamedev/gamedev-vm/azuredeploy.json +++ b/application-workloads/azure-gamedev/gamedev-vm/azuredeploy.json @@ -5,7 +5,7 @@ "_generator": { "name": "bicep", "version": "0.5.6.12127", - "templateHash": "5803441896698059175" + "templateHash": "8562583560199509810" } }, "parameters": { @@ -142,7 +142,7 @@ "_generator": { "name": "bicep", "version": "0.5.6.12127", - "templateHash": "6895246921804627188" + "templateHash": "8835447811081303294" } }, "parameters": { @@ -819,59 +819,112 @@ "_generator": { "name": "bicep", "version": "0.5.6.12127", - "templateHash": "10965725395189793781" + "templateHash": "10609471965789775318" } }, "parameters": { + "location": { + "type": "string", + "metadata": { + "description": "Deployment Location" + } + }, "_artifactsLocation": { "type": "string", - "defaultValue": "[deployment().properties.templateLink.uri]" + "defaultValue": "[deployment().properties.templateLink.uri]", + "metadata": { + "description": "The base URI where artifacts required by this template are located including a trailing '/'" + } }, "_artifactsLocationSasToken": { "type": "secureString", - "defaultValue": "" + "defaultValue": "", + "metadata": { + "description": "The sasToken required to access _artifactsLocation." + } }, "cmdGDKInstall": { - "type": "string" + "type": "string", + "metadata": { + "description": "Toggle to Install GDK" + } }, "cmdTeradiciRegistration": { - "type": "string" + "type": "string", + "metadata": { + "description": "Toggle to Register Teradici" + } }, "virtualMachineName": { - "type": "string" + "type": "string", + "metadata": { + "description": "Name of Game Developer Virtual Machine" + } }, "remoteAccessTechnology": { - "type": "string" - }, - "location": { - "type": "string" + "type": "string", + "allowedValues": [ + "RDP", + "Teradici", + "Parsec" + ], + "metadata": { + "description": "Remote Access technology" + } }, "fileShareStorageAccount": { - "type": "string" + "type": "string", + "metadata": { + "description": "Name of Storage Account to Link" + } }, "fileShareStorageAccountKey": { - "type": "secureString" + "type": "secureString", + "metadata": { + "description": "Key of Storage Account to Link" + } }, "fileShareName": { - "type": "string" + "type": "string", + "metadata": { + "description": "Name of File Share" + } }, "p4Port": { - "type": "string" + "type": "string", + "metadata": { + "description": "Port to use for p4" + } }, "p4Username": { - "type": "string" + "type": "string", + "metadata": { + "description": "Username for p4" + } }, "p4Password": { - "type": "secureString" + "type": "secureString", + "metadata": { + "description": "Password for p4" + } }, "p4Workspace": { - "type": "string" + "type": "string", + "metadata": { + "description": "p4 Workspace" + } }, "p4Stream": { - "type": "string" + "type": "string", + "metadata": { + "description": "Stream for p4" + } }, "p4ClientViews": { - "type": "string" + "type": "string", + "metadata": { + "description": "Client View for p4" + } } }, "variables": { From 8829d6be48fc66019b0a0e080b3ef7ec7a416148 Mon Sep 17 00:00:00 2001 From: Daniel Ciborowski Date: Fri, 22 Apr 2022 21:35:11 -0400 Subject: [PATCH 48/71] format --- .../gamedev-vm/nestedtemplates/remoteAccessExtension.bicep | 1 - 1 file changed, 1 deletion(-) diff --git a/application-workloads/azure-gamedev/gamedev-vm/nestedtemplates/remoteAccessExtension.bicep b/application-workloads/azure-gamedev/gamedev-vm/nestedtemplates/remoteAccessExtension.bicep index 632d22dfd696..3024e7cf9f4b 100644 --- a/application-workloads/azure-gamedev/gamedev-vm/nestedtemplates/remoteAccessExtension.bicep +++ b/application-workloads/azure-gamedev/gamedev-vm/nestedtemplates/remoteAccessExtension.bicep @@ -41,7 +41,6 @@ param p4Port string @description('Username for p4') param p4Username string - @description('Password for p4') @secure() param p4Password string From 96fae12af7b046e882a62813ba97b0ca83a3fc1d Mon Sep 17 00:00:00 2001 From: Daniel Ciborowski Date: Fri, 22 Apr 2022 22:01:16 -0400 Subject: [PATCH 49/71] Update gamedev-vm.bicep --- .../azure-gamedev/gamedev-vm/nestedtemplates/gamedev-vm.bicep | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application-workloads/azure-gamedev/gamedev-vm/nestedtemplates/gamedev-vm.bicep b/application-workloads/azure-gamedev/gamedev-vm/nestedtemplates/gamedev-vm.bicep index d101313657f9..3ec5db98ecc3 100644 --- a/application-workloads/azure-gamedev/gamedev-vm/nestedtemplates/gamedev-vm.bicep +++ b/application-workloads/azure-gamedev/gamedev-vm/nestedtemplates/gamedev-vm.bicep @@ -250,10 +250,10 @@ resource partnercenter 'Microsoft.Resources/deployments@2021-04-01' = { resource publicIp 'Microsoft.Network/publicIPAddresses@2021-05-01' = if (publicIpNewOrExisting == 'new') { name: publicIpName + location: location sku: { name: publicIpSku } - location: location properties: { publicIPAllocationMethod: publicIpAllocationMethod dnsSettings: { From 4470cf40d2b09060f7aab15fc05cc659d28ca002 Mon Sep 17 00:00:00 2001 From: Daniel Ciborowski Date: Fri, 22 Apr 2022 22:02:19 -0400 Subject: [PATCH 50/71] Apply suggestions from code review --- .../gamedev-vm/nestedtemplates/nsgRules.bicep | 3 --- .../remoteAccessExtension.bicep | 22 +++++++++---------- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/application-workloads/azure-gamedev/gamedev-vm/nestedtemplates/nsgRules.bicep b/application-workloads/azure-gamedev/gamedev-vm/nestedtemplates/nsgRules.bicep index fe254e912920..fdf14fa5826a 100644 --- a/application-workloads/azure-gamedev/gamedev-vm/nestedtemplates/nsgRules.bicep +++ b/application-workloads/azure-gamedev/gamedev-vm/nestedtemplates/nsgRules.bicep @@ -44,8 +44,6 @@ var nsgRules = { } } ] - - 'nsgRules-Teradici': !addPixelStreamingPorts ? [ { name: 'RDP' @@ -192,7 +190,6 @@ var nsgRules = { } } ] - 'nsgRules-Parsec': !addPixelStreamingPorts ? [ { name: 'RDP' diff --git a/application-workloads/azure-gamedev/gamedev-vm/nestedtemplates/remoteAccessExtension.bicep b/application-workloads/azure-gamedev/gamedev-vm/nestedtemplates/remoteAccessExtension.bicep index 3024e7cf9f4b..80c956ef9b89 100644 --- a/application-workloads/azure-gamedev/gamedev-vm/nestedtemplates/remoteAccessExtension.bicep +++ b/application-workloads/azure-gamedev/gamedev-vm/nestedtemplates/remoteAccessExtension.bicep @@ -60,9 +60,9 @@ var p4Params = '-p4Port \'${p4Port}\' -p4Username \'${p4Username}\' -p4Password var remoteAccessExtension = { RDP: { - publisher : 'Microsoft.Compute' - type : 'CustomScriptExtension' - typeHandlerVersion : '1.10' + publisher: 'Microsoft.Compute' + type: 'CustomScriptExtension' + typeHandlerVersion: '1.10' autoUpgradeMinorVersion: true settings: { fileUris: [ @@ -75,9 +75,9 @@ var remoteAccessExtension = { } } Teradici: { - publisher : 'Microsoft.Compute' - type : 'CustomScriptExtension' - typeHandlerVersion : '1.10' + publisher: 'Microsoft.Compute' + type: 'CustomScriptExtension' + typeHandlerVersion: '1.10' autoUpgradeMinorVersion: true settings: { fileUris: [ @@ -93,9 +93,9 @@ var remoteAccessExtension = { } } Parsec: { - publisher : 'Microsoft.Compute' - type : 'CustomScriptExtension' - typeHandlerVersion : '1.10' + publisher: 'Microsoft.Compute' + type: 'CustomScriptExtension' + typeHandlerVersion: '1.10' autoUpgradeMinorVersion: true settings: { fileUris: [ @@ -112,8 +112,8 @@ var remoteAccessExtension = { } resource virtualMachine_remoteAccessExtension 'Microsoft.Compute/virtualMachines/extensions@2021-11-01' = { - name : '${virtualMachineName}/CustomScriptExtension-${remoteAccessTechnology}' - location : location + name: '${virtualMachineName}/CustomScriptExtension-${remoteAccessTechnology}' + location: location properties: remoteAccessExtension[remoteAccessTechnology] } From 14d023fb853435821fdc43cb34b7890a882f773c Mon Sep 17 00:00:00 2001 From: Daniel Ciborowski Date: Fri, 22 Apr 2022 22:02:41 -0400 Subject: [PATCH 51/71] Update application-workloads/azure-gamedev/gamedev-vm/main.bicep --- application-workloads/azure-gamedev/gamedev-vm/main.bicep | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application-workloads/azure-gamedev/gamedev-vm/main.bicep b/application-workloads/azure-gamedev/gamedev-vm/main.bicep index e87d0f524a58..9bb837d32235 100644 --- a/application-workloads/azure-gamedev/gamedev-vm/main.bicep +++ b/application-workloads/azure-gamedev/gamedev-vm/main.bicep @@ -53,7 +53,7 @@ param passwordAdministratorLogin string param remoteAccessTechnology string = 'RDP' module gamedevvm './nestedtemplates/gamedev-vm.bicep' = { - name : 'gamingDevVM' + name: 'gamingDevVM' params: { location: location vmSize: vmSize From 3a18f4910c183ec35f49dfc9cccdd7b733ab9fa5 Mon Sep 17 00:00:00 2001 From: Daniel Ciborowski Date: Fri, 22 Apr 2022 22:03:02 -0400 Subject: [PATCH 52/71] Update application-workloads/azure-gamedev/gamedev-vm/nestedtemplates/gamedev-vm.bicep --- .../azure-gamedev/gamedev-vm/nestedtemplates/gamedev-vm.bicep | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application-workloads/azure-gamedev/gamedev-vm/nestedtemplates/gamedev-vm.bicep b/application-workloads/azure-gamedev/gamedev-vm/nestedtemplates/gamedev-vm.bicep index 3ec5db98ecc3..7b69f96c99b7 100644 --- a/application-workloads/azure-gamedev/gamedev-vm/nestedtemplates/gamedev-vm.bicep +++ b/application-workloads/azure-gamedev/gamedev-vm/nestedtemplates/gamedev-vm.bicep @@ -164,7 +164,7 @@ param unrealPixelStreamingEnabled bool = false param enableManagedIdentity bool = false @description('Enable Azure Active Directory Login') -param enableAAD bool = false +param enableAAD bool = false var environmentMapping = { ue_4_27: 'unreal_4_27' From ef3aa23a5d9d07785f47c126283bc7fe5cfa1c05 Mon Sep 17 00:00:00 2001 From: Daniel Ciborowski Date: Fri, 22 Apr 2022 22:10:01 -0400 Subject: [PATCH 53/71] Apply suggestions from code review --- application-workloads/azure-gamedev/gamedev-vm/metadata.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application-workloads/azure-gamedev/gamedev-vm/metadata.json b/application-workloads/azure-gamedev/gamedev-vm/metadata.json index a110e4a736a3..7c34da88b134 100644 --- a/application-workloads/azure-gamedev/gamedev-vm/metadata.json +++ b/application-workloads/azure-gamedev/gamedev-vm/metadata.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/azure-quickstart-templates-metadata-schema#", - "type": "Module", + "type": "QuickStart", "itemDisplayName": "Azure Game Developer Virtual Machine", "description": "Azure Game Developer Virtual Machine includes Licencsed Engines like Unreal.", "summary": "This template deploys an Azure Game Developer Virtual Machine.", From 34045b0c21713ab9569a8a74d4dcb433781684b0 Mon Sep 17 00:00:00 2001 From: Daniel Ciborowski Date: Mon, 25 Apr 2022 13:00:54 -0400 Subject: [PATCH 54/71] clean _var names --- .../azure-gamedev/gamedev-vm/azuredeploy.json | 45 +++++++++---------- .../nestedtemplates/gamedev-vm.bicep | 25 +++++------ 2 files changed, 34 insertions(+), 36 deletions(-) diff --git a/application-workloads/azure-gamedev/gamedev-vm/azuredeploy.json b/application-workloads/azure-gamedev/gamedev-vm/azuredeploy.json index 3f12b7cc7a3e..189b0a1d2f70 100644 --- a/application-workloads/azure-gamedev/gamedev-vm/azuredeploy.json +++ b/application-workloads/azure-gamedev/gamedev-vm/azuredeploy.json @@ -5,7 +5,7 @@ "_generator": { "name": "bicep", "version": "0.5.6.12127", - "templateHash": "8562583560199509810" + "templateHash": "14072181801581721908" } }, "parameters": { @@ -142,7 +142,7 @@ "_generator": { "name": "bicep", "version": "0.5.6.12127", - "templateHash": "8835447811081303294" + "templateHash": "18412523657805442082" } }, "parameters": { @@ -524,12 +524,11 @@ }, "vmImage": "[variables('environments')[parameters('environment')].vmImage]", "vmPlan": "[variables('environments')[parameters('environment')].vmPlan]", - "vmName_var": "[parameters('vmName')]", - "ipconfName": "[format('{0}-ipconf', variables('vmName_var'))]", - "nicName_var": "[format('{0}-nic', variables('vmName_var'))]", - "nsgName_var": "[format('{0}-nsg', variables('vmName_var'))]", - "storageType_var": "[if(bool(greater(length(split(parameters('vmSize'), '_')), 2)), 'Premium_LRS', 'Standard_LRS')]", - "tags_var": { + "ipconfName": "[format('{0}-ipconf', parameters('vmName'))]", + "nicName": "[format('{0}-nic', parameters('vmName'))]", + "nsgName": "[format('{0}-nsg', parameters('vmName'))]", + "storageType": "[if(bool(greater(length(split(parameters('vmSize'), '_')), 2)), 'Premium_LRS', 'Standard_LRS')]", + "tags": { "solution": "Game Development Virtual Machine", "engine": "[parameters('gameEngine')]", "ostype": "[parameters('osType')]", @@ -580,7 +579,7 @@ { "type": "Microsoft.Network/networkSecurityGroups", "apiVersion": "2021-05-01", - "name": "[variables('nsgName_var')]", + "name": "[variables('nsgName')]", "location": "[parameters('location')]", "properties": { "securityRules": "[reference(resourceId('Microsoft.Resources/deployments', 'nsg_rules')).outputs.nsgRules.value[format('nsgRules-{0}', parameters('remoteAccessTechnology'))]]" @@ -614,7 +613,7 @@ { "type": "Microsoft.Network/networkInterfaces", "apiVersion": "2021-05-01", - "name": "[variables('nicName_var')]", + "name": "[variables('nicName')]", "location": "[parameters('location')]", "properties": { "enableAcceleratedNetworking": "[if(bool(greater(length(split(parameters('vmSize'), '_')), 2)), true(), false())]", @@ -625,18 +624,18 @@ } ], "networkSecurityGroup": { - "id": "[resourceId('Microsoft.Network/networkSecurityGroups', variables('nsgName_var'))]" + "id": "[resourceId('Microsoft.Network/networkSecurityGroups', variables('nsgName'))]" } }, "dependsOn": [ - "[resourceId('Microsoft.Network/networkSecurityGroups', variables('nsgName_var'))]", + "[resourceId('Microsoft.Network/networkSecurityGroups', variables('nsgName'))]", "[resourceId('Microsoft.Network/virtualNetworks', parameters('vnetName'))]" ] }, { "type": "Microsoft.Compute/virtualMachines", "apiVersion": "2021-11-01", - "name": "[variables('vmName_var')]", + "name": "[parameters('vmName')]", "location": "[parameters('location')]", "plan": "[variables('vmPlan')]", "identity": "[if(or(parameters('enableAAD'), parameters('enableManagedIdentity')), createObject('type', 'systemAssigned'), null())]", @@ -658,17 +657,17 @@ ], "imageReference": "[variables('vmImage')]", "osDisk": { - "name": "[format('{0}-osdisk', variables('vmName_var'))]", + "name": "[format('{0}-osdisk', parameters('vmName'))]", "createOption": "FromImage", "caching": "ReadWrite", "diskSizeGB": 255, "managedDisk": { - "storageAccountType": "[variables('storageType_var')]" + "storageAccountType": "[variables('storageType')]" } } }, "osProfile": { - "computerName": "[variables('vmName_var')]", + "computerName": "[parameters('vmName')]", "adminUsername": "[parameters('adminName')]", "adminPassword": "[parameters('adminPass')]" }, @@ -676,21 +675,21 @@ "networkProfile": { "networkInterfaces": [ { - "id": "[resourceId('Microsoft.Network/networkInterfaces', variables('nicName_var'))]" + "id": "[resourceId('Microsoft.Network/networkInterfaces', variables('nicName'))]" } ] } }, - "tags": "[if(contains(parameters('outTagsByResource'), 'Microsoft.Compute/virtualMachines'), union(variables('tags_var'), parameters('outTagsByResource')['Microsoft.Compute/virtualMachines']), variables('tags_var'))]", + "tags": "[if(contains(parameters('outTagsByResource'), 'Microsoft.Compute/virtualMachines'), union(variables('tags'), parameters('outTagsByResource')['Microsoft.Compute/virtualMachines']), variables('tags'))]", "dependsOn": [ - "[resourceId('Microsoft.Network/networkInterfaces', variables('nicName_var'))]" + "[resourceId('Microsoft.Network/networkInterfaces', variables('nicName'))]" ] }, { "condition": "[parameters('enableAAD')]", "type": "Microsoft.Compute/virtualMachines/extensions", "apiVersion": "2021-11-01", - "name": "[format('{0}/AADLoginForWindows', variables('vmName_var'))]", + "name": "[format('{0}/AADLoginForWindows', parameters('vmName'))]", "location": "[parameters('location')]", "properties": { "publisher": "Microsoft.Azure.ActiveDirectory", @@ -700,7 +699,7 @@ }, "dependsOn": [ "[resourceId('Microsoft.Resources/deployments', 'runRemoteAccess')]", - "[resourceId('Microsoft.Compute/virtualMachines', variables('vmName_var'))]" + "[resourceId('Microsoft.Compute/virtualMachines', parameters('vmName'))]" ] }, { @@ -776,7 +775,7 @@ "value": "[parameters('_artifactsLocationSasToken')]" }, "virtualMachineName": { - "value": "[variables('vmName_var')]" + "value": "[parameters('vmName')]" }, "remoteAccessTechnology": { "value": "[parameters('remoteAccessTechnology')]" @@ -1001,7 +1000,7 @@ } }, "dependsOn": [ - "[resourceId('Microsoft.Compute/virtualMachines', variables('vmName_var'))]" + "[resourceId('Microsoft.Compute/virtualMachines', parameters('vmName'))]" ] } ], diff --git a/application-workloads/azure-gamedev/gamedev-vm/nestedtemplates/gamedev-vm.bicep b/application-workloads/azure-gamedev/gamedev-vm/nestedtemplates/gamedev-vm.bicep index 7b69f96c99b7..d929edd70ebd 100644 --- a/application-workloads/azure-gamedev/gamedev-vm/nestedtemplates/gamedev-vm.bicep +++ b/application-workloads/azure-gamedev/gamedev-vm/nestedtemplates/gamedev-vm.bicep @@ -204,14 +204,13 @@ var environments = { var vmImage = environments[environment].vmImage var vmPlan = environments[environment].vmPlan -var vmName_var = vmName -var ipconfName = '${vmName_var}-ipconf' -var nicName_var = '${vmName_var}-nic' -var nsgName_var = '${vmName_var}-nsg' +var ipconfName = '${vmName}-ipconf' +var nicName = '${vmName}-nic' +var nsgName = '${vmName}-nsg' -var storageType_var = (bool(length(split(vmSize, '_')) > 2) ? 'Premium_LRS' : 'Standard_LRS') +var storageType = (bool(length(split(vmSize, '_')) > 2) ? 'Premium_LRS' : 'Standard_LRS') -var tags_var = { +var tags = { 'solution': 'Game Development Virtual Machine' 'engine': gameEngine 'ostype': osType @@ -270,7 +269,7 @@ module nsg_rules 'nsgRules.bicep' = { } resource nsg 'Microsoft.Network/networkSecurityGroups@2021-05-01' = { - name: nsgName_var + name: nsgName location: location properties: { securityRules: nsg_rules.outputs.nsgRules['nsgRules-${remoteAccessTechnology}'] @@ -298,7 +297,7 @@ resource vnet 'Microsoft.Network/virtualNetworks@2021-05-01' = if (vnetNewOrExis } resource nic 'Microsoft.Network/networkInterfaces@2021-05-01' = { - name: nicName_var + name: nicName location: location dependsOn: [ vnet @@ -328,7 +327,7 @@ resource nic 'Microsoft.Network/networkInterfaces@2021-05-01' = { } resource virtualMachine 'Microsoft.Compute/virtualMachines@2021-11-01' = { - name: vmName_var + name: vmName location: location plan: vmPlan identity: enableAAD || enableManagedIdentity ? { @@ -341,12 +340,12 @@ resource virtualMachine 'Microsoft.Compute/virtualMachines@2021-11-01' = { storageProfile: { imageReference: vmImage osDisk: { - name: '${vmName_var}-osdisk' + name: '${vmName}-osdisk' createOption: 'FromImage' caching: 'ReadWrite' diskSizeGB: 255 managedDisk: { - storageAccountType: storageType_var + storageAccountType: storageType } } dataDisks: [for i in range(0, numDataDisks): { @@ -356,7 +355,7 @@ resource virtualMachine 'Microsoft.Compute/virtualMachines@2021-11-01' = { }] } osProfile: { - computerName: vmName_var + computerName: vmName adminUsername: adminName adminPassword: adminPass } @@ -369,7 +368,7 @@ resource virtualMachine 'Microsoft.Compute/virtualMachines@2021-11-01' = { ] } } - tags: (contains(outTagsByResource, 'Microsoft.Compute/virtualMachines') ? union(tags_var, outTagsByResource['Microsoft.Compute/virtualMachines']) : tags_var) + tags: (contains(outTagsByResource, 'Microsoft.Compute/virtualMachines') ? union(tags, outTagsByResource['Microsoft.Compute/virtualMachines']) : tags) } module remoteAccess 'remoteAccessExtension.bicep' = { From 4d4b302cecff9c51c41fefd46291f63e93a32d62 Mon Sep 17 00:00:00 2001 From: Daniel Ciborowski Date: Tue, 26 Apr 2022 08:38:13 -0400 Subject: [PATCH 55/71] Create gpt.ini --- .../azure-gamedev/gamedev-vm/scripts/parsec/gpt.ini | 1 + 1 file changed, 1 insertion(+) create mode 100644 application-workloads/azure-gamedev/gamedev-vm/scripts/parsec/gpt.ini diff --git a/application-workloads/azure-gamedev/gamedev-vm/scripts/parsec/gpt.ini b/application-workloads/azure-gamedev/gamedev-vm/scripts/parsec/gpt.ini new file mode 100644 index 000000000000..8b137891791f --- /dev/null +++ b/application-workloads/azure-gamedev/gamedev-vm/scripts/parsec/gpt.ini @@ -0,0 +1 @@ + From 49f02a63a1f56f468228f34aa3da4bef0945cf18 Mon Sep 17 00:00:00 2001 From: Daniel Ciborowski Date: Tue, 26 Apr 2022 07:38:47 -0500 Subject: [PATCH 56/71] Add files via upload --- .../scripts/parsec/Automatic-Shutdown.ps1 | 136 +++++++++ .../gamedev-vm/scripts/parsec/Clear-Proxy.ps1 | 271 ++++++++++++++++++ .../CreateAutomaticShutdownScheduledTask.ps1 | 34 +++ .../parsec/CreateClearProxyScheduledTask.ps1 | 50 ++++ .../CreateOneHourWarningScheduledTask.ps1 | 56 ++++ .../scripts/parsec/ForceCloseShutDown.reg | 50 ++++ .../gamedev-vm/scripts/parsec/GPU-Update.ico | Bin 0 -> 50762 bytes .../scripts/parsec/NetworkRestore.ps1 | 70 +++++ .../scripts/parsec/NetworkRestore.reg | Bin 0 -> 1726 bytes .../gamedev-vm/scripts/parsec/OneHour.ps1 | 17 ++ .../gamedev-vm/scripts/parsec/Parsec.png | Bin 0 -> 6702 bytes .../gamedev-vm/scripts/parsec/ShowDialog.ps1 | 117 ++++++++ .../scripts/parsec/TeamMachineSetup.ps1 | 198 +++++++++++++ .../scripts/parsec/WarningMessage.ps1 | 24 ++ .../gamedev-vm/scripts/parsec/gpt.ini | 4 +- .../gamedev-vm/scripts/parsec/psscripts.ini | Bin 0 -> 112 bytes 16 files changed, 1026 insertions(+), 1 deletion(-) create mode 100644 application-workloads/azure-gamedev/gamedev-vm/scripts/parsec/Automatic-Shutdown.ps1 create mode 100644 application-workloads/azure-gamedev/gamedev-vm/scripts/parsec/Clear-Proxy.ps1 create mode 100644 application-workloads/azure-gamedev/gamedev-vm/scripts/parsec/CreateAutomaticShutdownScheduledTask.ps1 create mode 100644 application-workloads/azure-gamedev/gamedev-vm/scripts/parsec/CreateClearProxyScheduledTask.ps1 create mode 100644 application-workloads/azure-gamedev/gamedev-vm/scripts/parsec/CreateOneHourWarningScheduledTask.ps1 create mode 100644 application-workloads/azure-gamedev/gamedev-vm/scripts/parsec/ForceCloseShutDown.reg create mode 100644 application-workloads/azure-gamedev/gamedev-vm/scripts/parsec/GPU-Update.ico create mode 100644 application-workloads/azure-gamedev/gamedev-vm/scripts/parsec/NetworkRestore.ps1 create mode 100644 application-workloads/azure-gamedev/gamedev-vm/scripts/parsec/NetworkRestore.reg create mode 100644 application-workloads/azure-gamedev/gamedev-vm/scripts/parsec/OneHour.ps1 create mode 100644 application-workloads/azure-gamedev/gamedev-vm/scripts/parsec/Parsec.png create mode 100644 application-workloads/azure-gamedev/gamedev-vm/scripts/parsec/ShowDialog.ps1 create mode 100644 application-workloads/azure-gamedev/gamedev-vm/scripts/parsec/TeamMachineSetup.ps1 create mode 100644 application-workloads/azure-gamedev/gamedev-vm/scripts/parsec/WarningMessage.ps1 create mode 100644 application-workloads/azure-gamedev/gamedev-vm/scripts/parsec/psscripts.ini diff --git a/application-workloads/azure-gamedev/gamedev-vm/scripts/parsec/Automatic-Shutdown.ps1 b/application-workloads/azure-gamedev/gamedev-vm/scripts/parsec/Automatic-Shutdown.ps1 new file mode 100644 index 000000000000..479f3bcc68ea --- /dev/null +++ b/application-workloads/azure-gamedev/gamedev-vm/scripts/parsec/Automatic-Shutdown.ps1 @@ -0,0 +1,136 @@ +Add-Type @' +using System; +using System.Diagnostics; +using System.Runtime.InteropServices; + +namespace PInvoke.Win32 { + + public static class UserInput { + + [DllImport("user32.dll", SetLastError=false)] + private static extern bool GetLastInputInfo(ref LASTINPUTINFO plii); + + [StructLayout(LayoutKind.Sequential)] + private struct LASTINPUTINFO { + public uint cbSize; + public int dwTime; + } + + public static DateTime LastInput { + get { + DateTime bootTime = DateTime.UtcNow.AddMilliseconds(-Environment.TickCount); + DateTime lastInput = bootTime.AddMilliseconds(LastInputTicks); + return lastInput; + } + } + + public static TimeSpan IdleTime { + get { + return DateTime.UtcNow.Subtract(LastInput); + } + } + + public static int LastInputTicks { + get { + LASTINPUTINFO lii = new LASTINPUTINFO(); + lii.cbSize = (uint)Marshal.SizeOf(typeof(LASTINPUTINFO)); + GetLastInputInfo(ref lii); + return lii.dwTime; + } + } + } +} +'@ +function AutomaticShutdown { + + Add-Type -AssemblyName System.Windows.Forms + + [System.Windows.Forms.Application]::EnableVisualStyles() + $form1 = New-Object System.Windows.Forms.Form + $label000000 = New-Object System.Windows.Forms.Label + $timer1 = New-Object System.Windows.Forms.Timer + $button = New-Object System.Windows.Forms.Button + $InitialFormWindowState = New-Object System.Windows.Forms.FormWindowState + + + + $form1_Load = { + $script:countdown = [timespan]'00:10:00' # 10 minutes + $label000000.Text = "$countdown" + $timer1.Start() + + } + + $button_logic = { + Start-Process powershell.exe -ArgumentList "-ExecutionPolicy Bypass -windowstyle hidden -file $ENV:ProgramData\Automatic-Shutdown.ps1" + $form1.Close() + } + + $timer1_Tick = { + if ($countdown -lt [timespan]'00:00:02') {$timer1.Stop() + Stop-Computer + } + Else{} + $script:countdown -= [timespan]'00:00:01' + $label000000.Text = "$countdown" + + } + + $Form_StateCorrection_Load = + { + #Correct the initial state of the form to prevent the .Net maximized form issue + $form1.WindowState = $InitialFormWindowState + } + + + $form1.SuspendLayout() + $form1.BringToFront() + $Form1.ControlBox = $False + $form1.Controls.Add($label000000) + $form1.AutoScaleDimensions = '8, 17' + $form1.AutoScaleMode = 'Font' + $form1.BackColor = 'ActiveCaption' + $form1.ClientSize = '400, 200' + $form1.FormBorderStyle = 'Fixed3D' + $form1.Name = 'form1' + $form1.StartPosition = 'CenterScreen' + $form1.Text = 'Automatic Shutdown On Idle' + $form1.add_Load($form1_Load) + + $Button.Location = New-Object System.Drawing.Size(75,75) + $Button.Size = New-Object System.Drawing.Size(150,23) + $Button.Text = "Cancel Shutdown" + $button.Add_click($button_logic) + + + $label000000.AutoSize = $True + $label000000.Font = 'Lucida Fax, 24pt, style=Bold' + $label000000.Location = '90, 25' + $label000000.Margin = '4, 0, 4, 0' + $label000000.Name = 'label000000' + $label000000.Size = '200, 46' + $label000000.TabIndex = 0 + $label000000.Text = '00:00:00' + + $timer1.Interval = 1000 + $timer1.add_Tick($timer1_Tick) + $form1.ResumeLayout() + + $InitialFormWindowState = $form1.WindowState + $form1.add_Load($Form_StateCorrection_Load) + $Form1.Controls.Add($Button) + return $form1.ShowDialog() +} + +function idle { +$readfile = (Get-Content -Path $env:ProgramData\Autoshutdown.txt) - 10 +do { +[PInvoke.Win32.UserInput]::LastInput | Out-Null +[PInvoke.Win32.UserInput]::IdleTime | Out-Null +Start-Sleep -Seconds 1 +} +Until([PInvoke.Win32.UserInput]::Idletime.TotalMinutes -gt $readfile) +AutomaticShutdown +} + +idle \ No newline at end of file diff --git a/application-workloads/azure-gamedev/gamedev-vm/scripts/parsec/Clear-Proxy.ps1 b/application-workloads/azure-gamedev/gamedev-vm/scripts/parsec/Clear-Proxy.ps1 new file mode 100644 index 000000000000..58eda36cae31 --- /dev/null +++ b/application-workloads/azure-gamedev/gamedev-vm/scripts/parsec/Clear-Proxy.ps1 @@ -0,0 +1,271 @@ +#clear windows proxy + +$size = (Get-PartitionSupportedSize -DiskNumber 0 -PartitionNumber 1) +[System.Uint64]$currentsize = (Get-Partition -DiskNumber 0 -PartitionNumber 1).Size +[System.Uint64]$maxpartitionsize = ($size.SizeMax).ToString() + +if ($($currentsize) -ge $($maxpartitionsize)) { +"Hard Drive already expanded" +} +Else +{ +Resize-Partition -DiskNumber 0 -PartitionNumber 1 -Size $size.SizeMax +"Successfully Increased Partition Size" +} + + +function clear-proxy { +$value = Get-ItemProperty 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings' -name ProxyEnable +if ($value.ProxyEnable -eq 1) { +set-itemproperty 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings' -name ProxyEnable -value 0 | Out-Null +write-host "Disable proxy if required" +Start-Process "C:\Program Files\Internet Explorer\iexplore.exe" +Start-Sleep -s 5 +Get-Process iexplore | Foreach-Object { $_.CloseMainWindow() | Out-Null } | stop-process –force} +Else {}} + +#fix gpu + +function EnableDisabledGPU { +$getdisabled = Get-WmiObject win32_pnpentity | Where-Object {$_.name -like '*NVIDIA*' -or $_.name -like '3D Video Controller' -and $_.status -like 'Error'} | Select-Object -ExpandProperty PNPDeviceID +if ($getdisabled -ne $null) {"Enabling GPU" +$var = $getdisabled.Substring(0,21) +$arguement = "/r enable"+ ' ' + "*"+ "$var"+ "*" +Start-Process -FilePath "C:\ParsecTemp\Devcon\devcon.exe" -ArgumentList $arguement +} +Else {"Device is enabled" +Start-Process -FilePath "C:\ParsecTemp\Devcon\devcon.exe" -ArgumentList '/m /r'} +} + + + +function installedGPUID { +#queries WMI to get DeviceID of the installed NVIDIA GPU +Try {(get-wmiobject -query "select DeviceID from Win32_PNPEntity Where (deviceid Like '%PCI\\VEN_10DE%') and (PNPClass = 'Display' or Name = '3D Video Controller')" | Select-Object DeviceID -ExpandProperty DeviceID).substring(13,8)} +Catch {return $null} +} + +function driverVersion { +#Queries WMI to request the driver version, and formats it to match that of a NVIDIA Driver version number (NNN.NN) +Try {(Get-WmiObject Win32_PnPSignedDriver | where {$_.DeviceName -like "*nvidia*" -and $_.DeviceClass -like "Display"} | Select-Object -ExpandProperty DriverVersion).substring(7,6).replace('.','').Insert(3,'.')} +Catch {return $null} +} + +function osVersion { +#Requests Windows OS Friendly Name +(Get-WmiObject -class Win32_OperatingSystem).Caption +} + +function requiresReboot{ +#Queries if system needs a reboot after driver installs +if (Get-ChildItem "HKLM:\Software\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootPending" -EA Ignore) { return $true } +if (Get-Item "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired" -EA Ignore) { return $true } +if (Get-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager" -Name PendingFileRenameOperations -EA Ignore) { return $true } + try { + $util = [wmiclass]"\\.\root\ccm\clientsdk:CCM_ClientUtilities" + $status = $util.DetermineIfRebootPending() + if(($status -ne $null) -and $status.RebootPending){ + return $true + } + }catch{} + + return $false +} + +function validDriver { +#checks an important nvidia driver folder to see if it exits +test-path -Path "C:\Program Files\NVIDIA Corporation\NVSMI" +} + +Function webDriver { +#checks the latest available graphics driver from nvidia.com +if (($gpu.supported -eq "No") -eq $true) {"Sorry, this GPU (" + $gpu.name + ") is not yet supported by this tool." +Exit +} +Elseif (($gpu.Supported -eq "UnOfficial") -eq $true) { +if ($url.GoogleGRID -eq $null) {$URL.GoogleGRID = Invoke-WebRequest -uri https://cloud.google.com/compute/docs/gpus/add-gpus#installing_grid_drivers_for_virtual_workstations -UseBasicParsing} Else {} +$($($URL.GoogleGRID).Links | Where-Object href -like *server2016_64bit_international.exe*).outerHTML.Split('/')[6].split('_')[0] +} +Else { +$gpu.URL = "https://www.nvidia.com/Download/processFind.aspx?psid=" + $gpu.psid + "&pfid=" + $gpu.pfid + "&osid=" + $gpu.osid + "&lid=1&whql=1&lang=en-us&ctk=0" +$link = Invoke-WebRequest -Uri $gpu.URL -Method GET -UseBasicParsing +$link -match '([^<]+?)' | Out-Null +if (($matches[1] -like "*(*") -eq $true) {$matches[1].split('(')[1].split(')')[0]} +Else {$matches[1]} +} +} + +function GPUCurrentMode { +#returns if the GPU is running in TCC or WDDM mode +$nvidiaarg = "-i 0 --query-gpu=driver_model.current --format=csv,noheader" +$nvidiasmi = "c:\program files\nvidia corporation\nvsmi\nvidia-smi" +try {Invoke-Expression "& `"$nvidiasmi`" $nvidiaarg"} +catch {$null} +} + +function queryOS { +#sets OS support +if (($system.OS_Version -like "*Windows 10*") -eq $true) {$gpu.OSID = '57' ; $system.OS_Supported = $false} +elseif (($system.OS_Version -like "*Windows 8.1*") -eq $true) {$gpu.OSID = "41"; $system.OS_Supported = $false} +elseif (($system.OS_Version -like "*Server 2016*") -eq $true) {$gpu.OSID = "74"; $system.OS_Supported = $true} +elseif (($system.OS_Version -like "*Server 2019*") -eq $true) {$gpu.OSID = "74"; $system.OS_Supported = $true} +Else {$system.OS_Supported = $false} +} + +function webName { +#Gets the unknown GPU name from a csv based on a deviceID found in the installedgpuid function +(New-Object System.Net.WebClient).DownloadFile("https://raw.githubusercontent.com/parsec-cloud/Cloud-GPU-Updater/master/Additional%20Files/GPUID.csv", $($system.Path + "\GPUID.CSV")) +Import-Csv "$($system.path)\GPUID.csv" -Delimiter ',' | Where-Object DeviceID -like *$($gpu.Device_ID)* | Select-Object -ExpandProperty GPUName +} + +function queryGPU { +#sets details about current gpu +if($gpu.Device_ID -eq "DEV_13F2") {$gpu.Name = 'NVIDIA Tesla M60'; $gpu.PSID = '75'; $gpu.PFID = '783'; $gpu.NV_GRID = $true; $gpu.Driver_Version = driverversion; $gpu.Web_Driver = webdriver; $gpu.Update_Available = ($gpu.Web_Driver -gt $gpu.Driver_Version); $gpu.Current_Mode = GPUCurrentMode; $gpu.Supported = "Yes"} +ElseIF($gpu.Device_ID -eq "DEV_118A") {$gpu.Name = 'NVIDIA GRID K520'; $gpu.PSID = '94'; $gpu.PFID = '704'; $gpu.NV_GRID = $true; $gpu.Driver_Version = driverversion; $gpu.Web_Driver = webdriver; $gpu.Update_Available = ($gpu.Web_Driver -gt $gpu.Driver_Version); $gpu.Current_Mode = GPUCurrentMode; $gpu.Supported = "Yes"} +ElseIF($gpu.Device_ID -eq "DEV_1BB1") {$gpu.Name = 'NVIDIA Quadro P4000'; $gpu.PSID = '73'; $gpu.PFID = '840'; $gpu.NV_GRID = $false; $gpu.Driver_Version = driverversion; $gpu.Web_Driver = webdriver; $gpu.Update_Available = ($gpu.Web_Driver -gt $gpu.Driver_Version); $gpu.Current_Mode = GPUCurrentMode; $gpu.Supported = "Yes"} +Elseif($gpu.Device_ID -eq "DEV_1BB0") {$gpu.Name = 'NVIDIA Quadro P5000'; $gpu.PSID = '73'; $gpu.PFID = '823'; $gpu.NV_GRID = $false; $gpu.Driver_Version = driverversion; $gpu.Web_Driver = webdriver; $gpu.Update_Available = ($gpu.Web_Driver -gt $gpu.Driver_Version); $gpu.Current_Mode = GPUCurrentMode; $gpu.Supported = "Yes"} +Elseif($gpu.Device_ID -eq "DEV_15F8") {$gpu.Name = 'NVIDIA Tesla P100'; $gpu.PSID = '103'; $gpu.PFID = '822'; $gpu.NV_GRID = $true; $gpu.Driver_Version = driverversion; $gpu.Web_Driver = webdriver; $gpu.Update_Available = ($gpu.Web_Driver -gt $gpu.Driver_Version); $gpu.Current_Mode = GPUCurrentMode; $gpu.Supported = "UnOfficial"} +Elseif($gpu.Device_ID -eq "DEV_1BB3") {$gpu.Name = 'NVIDIA Tesla P4'; $gpu.PSID = '103'; $gpu.PFID = '831'; $gpu.NV_GRID = $true; $gpu.Driver_Version = driverversion; $gpu.Web_Driver = webdriver; $gpu.Update_Available = ($gpu.Web_Driver -gt $gpu.Driver_Version); $gpu.Current_Mode = GPUCurrentMode; $gpu.Supported = "UnOfficial"} +Elseif($gpu.Device_ID -eq "DEV_1EB8") {$gpu.Name = 'NVIDIA Tesla T4'; $gpu.PSID = '110'; $gpu.PFID = '883'; $gpu.NV_GRID = $true; $gpu.Driver_Version = driverversion; $gpu.Web_Driver = webdriver; $gpu.Update_Available = ($gpu.Web_Driver -gt $gpu.Driver_Version); $gpu.Current_Mode = GPUCurrentMode; $gpu.Supported = "UnOfficial"} +Elseif($gpu.Device_ID -eq $null) {$gpu.Supported = "No"; $gpu.Name = "No Device Found"} +else{$gpu.Supported = "No"; $gpu.Name = webName} +} + +function checkGPUSupport{ +#quits if GPU isn't supported +If ($gpu.Supported -eq "No") { +$app.FailGPU +Exit +} +ElseIf ($gpu.Supported -eq "UnOfficial") { +$app.UnOfficialGPU +} +Else{} +} + +function checkDriverInstalled { +#Tells user if no GPU driver is installed +if ($system.Valid_NVIDIA_Driver -eq $False) { +$app.NoDriver +} +Else{} +} + +function prepareEnvironment { +#prepares working directory +$test = Test-Path -Path $system.path +if ($test -eq $true) { +Remove-Item -path $system.Path -Recurse -Force | Out-Null +New-Item -ItemType Directory -Force -Path $system.path | Out-Null} +Else { +New-Item -ItemType Directory -Force -Path $system.path | Out-Null +} +} + +function startUpdate { +#Gives user an option to start the update, and sends messages to the user + prepareEnvironment + downloaddriver + InstallDriver + rebootlogic +} + +function setnvsmi { +#downloads script to set GPU to WDDM if required +(New-Object System.Net.WebClient).DownloadFile("https://raw.githubusercontent.com/parsec-cloud/Cloud-GPU-Updater/master/Additional%20Files/NVSMI.ps1", $($system.Path) + "\NVSMI.ps1") +Unblock-File -Path "$($system.Path)\NVSMI.ps1" +} + +function setnvsmi-shortcut{ +#creates startup shortcut that will start the script downloaded in setnvsmi +Write-Output "Create NVSMI shortcut" +$Shell = New-Object -ComObject ("WScript.Shell") +$ShortCut = $Shell.CreateShortcut("$env:PROGRAMDATA\Microsoft\Windows\Start Menu\Programs\Startup\NVSMI.lnk") +$ShortCut.TargetPath="powershell.exe" +$ShortCut.Arguments='-WindowStyle hidden -ExecutionPolicy Bypass -File "C:\ParsecTemp\Drivers\NVSMI.ps1"' +$ShortCut.WorkingDirectory = "C:\ParsecTemp\Drivers"; +$ShortCut.WindowStyle = 0; +$ShortCut.Description = "Create NVSMI shortcut"; +$ShortCut.Save() +} + +function DownloadDriver { +if (($gpu.supported -eq "UnOfficial") -eq $true) { +(New-Object System.Net.WebClient).DownloadFile($($($URL.GoogleGRID).links | Where-Object href -like *server2016_64bit_international.exe*).href, "C:\ParsecTemp\Drivers\GoogleGRID.exe") +} +Else { +#downloads driver from nvidia.com +$Download.Link = Invoke-WebRequest -Uri $gpu.url -Method Get -UseBasicParsing | select @{N='Latest';E={$($_.links.href -match"www.nvidia.com/download/driverResults.aspx*")[0].substring(2)}} +$download.Direct = Invoke-WebRequest -Uri $download.link.latest -Method Get -UseBasicParsing | select @{N= 'Download'; E={"http://us.download.nvidia.com" + $($_.links.href -match "/content/driverdownload*").split('=')[1].split('&')[0]}} +(New-Object System.Net.WebClient).DownloadFile($($download.direct.download), $($system.Path) + "\NVIDIA_" + $($gpu.web_driver) + ".exe") +} +} + +function installDriver { +#installs driver silently with /s /n arguments provided by NVIDIA +$DLpath = Get-ChildItem -Path $system.path -Include *exe* -Recurse | Select-Object -ExpandProperty Name +Start-Process -FilePath "$($system.Path)\$dlpath" -ArgumentList "/s /n" -Wait } + +#setting up arrays below +$url = @{} +$download = @{} +$app = @{} +$gpu = @{Device_ID = installedGPUID} +$system = @{Valid_NVIDIA_Driver = ValidDriver; OS_Version = osVersion; OS_Reboot_Required = RequiresReboot; Date = get-date; Path = "C:\ParsecTemp\Drivers"} + +function rebootLogic { +#checks if machine needs to be rebooted, and sets a startup item to set GPU mode to WDDM if required +if ($system.OS_Reboot_Required -eq $true) { + if ($GPU.NV_GRID -eq $false) + { + start-sleep -s 10 + Restart-Computer -Force} + ElseIf ($GPU.NV_GRID -eq $true) { + setnvsmi + setnvsmi-shortcut + start-sleep -s 10 + Restart-Computer -Force} + Else{} +} +Else { + if ($gpu.NV_GRID -eq $true) { + setnvsmi + setnvsmi-shortcut + start-sleep -s 10 + Restart-Computer -Force} + ElseIf ($gpu.NV_GRID -eq $false) { + } + Else{} +} +} + +#remove Windows Proxy +clear-proxy + +#fix gpu +EnableDisabledGPU +prepareEnvironment +queryOS +querygpu +querygpu +checkGPUSupport +querygpu + +if(($gpu.supported -eq "Yes") -or ($gpu.supported -eq "UnOfficial")) {} +Else { +Write-host "There is no GPU or it is unsupported" +Exit +} + +if ($gpu.driver_version -eq $null) { +write-host "No Driver" +startUpdate +} +Else{"Continue"} +if ($gpu.current_mode -eq "TCC") { +write-host "Change Driver Mode" +setnvsmi +setnvsmi-shortcut +shutdown /r -t 0} +Else {} + diff --git a/application-workloads/azure-gamedev/gamedev-vm/scripts/parsec/CreateAutomaticShutdownScheduledTask.ps1 b/application-workloads/azure-gamedev/gamedev-vm/scripts/parsec/CreateAutomaticShutdownScheduledTask.ps1 new file mode 100644 index 000000000000..a3967785df82 --- /dev/null +++ b/application-workloads/azure-gamedev/gamedev-vm/scripts/parsec/CreateAutomaticShutdownScheduledTask.ps1 @@ -0,0 +1,34 @@ +# Self-elevate the script if required +if (-Not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] 'Administrator')) { + if ([int](Get-CimInstance -Class Win32_OperatingSystem | Select-Object -ExpandProperty BuildNumber) -ge 6000) { + $CommandLine = "-File `"" + $MyInvocation.MyCommand.Path + "`" " + $MyInvocation.UnboundArguments + Start-Process -FilePath PowerShell.exe -Verb Runas -ArgumentList $CommandLine + Exit + } +} + +Write-Host "This sets your machine to shutdown if Windows detects it as idle for X minutes. +This is intended to save you money if you ever forget to shut your machine down. +You will get a warning message pop up 10 minutes before shutdown" + +Do {[int]$read = read-host "How much time should the system idle for before shutting down? Time in Minutes - Minimum 20"} +while ($read -lt "20") +$read | Out-File $env:Programdata\ParsecLoader\Autoshutdown.txt +$readfile = Get-Content -Path $env:Programdata\ParsecLoader\Autoshutdown.txt +$time = $readfile - 10 +$span = new-timespan -minutes $time + +try {Get-ScheduledTask -TaskName "Automatically Shutdown on Idle" -ErrorAction Stop | Out-Null +Unregister-ScheduledTask -TaskName "Automatically Shutdown on Idle" -Confirm:$false +} +catch {} + +$action = New-ScheduledTaskAction -Execute 'C:\WINDOWS\system32\WindowsPowerShell\v1.0\powershell.exe' -Argument '-executionpolicy bypass -windowstyle hidden -file %programdata%\ParsecLoader\automatic-shutdown.ps1' + +$trigger = New-ScheduledTaskTrigger -AtLogOn -User $env:USERNAME + +Register-ScheduledTask -Action $action -Trigger $trigger -TaskName "Automatically Shutdown on Idle" -Description "This script runs at startup and monitors for idle" -RunLevel Highest + +Write-Output "Successfully Created" + +pause diff --git a/application-workloads/azure-gamedev/gamedev-vm/scripts/parsec/CreateClearProxyScheduledTask.ps1 b/application-workloads/azure-gamedev/gamedev-vm/scripts/parsec/CreateClearProxyScheduledTask.ps1 new file mode 100644 index 000000000000..fca8c0a38c9d --- /dev/null +++ b/application-workloads/azure-gamedev/gamedev-vm/scripts/parsec/CreateClearProxyScheduledTask.ps1 @@ -0,0 +1,50 @@ +# Self-elevate the script if required +if (-Not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] 'Administrator')) { + if ([int](Get-CimInstance -Class Win32_OperatingSystem | Select-Object -ExpandProperty BuildNumber) -ge 6000) { + $CommandLine = "-File `"" + $MyInvocation.MyCommand.Path + "`" " + $MyInvocation.UnboundArguments + Start-Process -FilePath PowerShell.exe -Verb Runas -ArgumentList $CommandLine + Exit + } +} + +function Setup { +#requests user approve potential cloud run time charges for using the tool +Write-Output "This creates a scheduled task that +automatically disables Windows Proxies and re-enables +and installs the GPU driver if a user accidentally deletes +it. Both of these stop Parsec working. Runs on Startup. + +If the user does uninstall the driver, the machine may +reboot automatically. No warranty given or implied." +$ReadHost = Read-Host "Install the scheduled task? (Y/N)" + Switch ($ReadHost) + { + Y { + Write-output "Creating Task" + CreateScheduledTask | Out-Null + Write-output "Done" + Pause + Exit + } + N{ + Write-Output "The upgrade script will now exit" + Pause + Exit} + } +} + +Function CreateScheduledTask { + +try {Get-ScheduledTask -TaskName "Recover GPU Driver and Remove Proxy" -ErrorAction Stop | Out-Null +Unregister-ScheduledTask -TaskName "Recover GPU Driver and Remove Proxy" -Confirm:$false +} +catch {} + +$action = New-ScheduledTaskAction -Execute 'C:\WINDOWS\system32\WindowsPowerShell\v1.0\powershell.exe' -Argument '-file %programdata%\ParsecLoader\clear-proxy.ps1' + +$trigger = New-ScheduledTaskTrigger -AtLogOn -User $env:USERNAME + +Register-ScheduledTask -Action $action -Trigger $trigger -TaskName "Recover GPU Driver and Remove Proxy" -Description "This task reinstalls or re-enables the GPU and clears any Windows Proxies" -RunLevel Highest +} + +setup \ No newline at end of file diff --git a/application-workloads/azure-gamedev/gamedev-vm/scripts/parsec/CreateOneHourWarningScheduledTask.ps1 b/application-workloads/azure-gamedev/gamedev-vm/scripts/parsec/CreateOneHourWarningScheduledTask.ps1 new file mode 100644 index 000000000000..ba2bf1789ca0 --- /dev/null +++ b/application-workloads/azure-gamedev/gamedev-vm/scripts/parsec/CreateOneHourWarningScheduledTask.ps1 @@ -0,0 +1,56 @@ +# Self-elevate the script if required +if (-Not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] 'Administrator')) { + if ([int](Get-CimInstance -Class Win32_OperatingSystem | Select-Object -ExpandProperty BuildNumber) -ge 6000) { + $CommandLine = "-File `"" + $MyInvocation.MyCommand.Path + "`" " + $MyInvocation.UnboundArguments + Start-Process -FilePath PowerShell.exe -Verb Runas -ArgumentList $CommandLine + Exit + } +} + +Write-Host "This script sets your machine to warn you 5 minutes before your machine bills you for an hour, +This is useful if your Machine bills per hour, like AWS" + +$confirmation = Read-Host "Are you Sure You Want To Proceed: Y/N" +Switch ($confirmation) { + Y { + } + N { + Exit + } + } + + +try { + Get-ScheduledTask -TaskName "One Hour Warning Message" -ErrorAction Stop | Out-Null + $ModifyOrRemove = "You already have the script installed, remove it?" + Switch ($ModifyOrRemove) { + N { + } + Y { + Unregister-ScheduledTask -TaskName "One Hour Warning Message" -Confirm:$false + "The warning message has been removed" + Pause + Exit} + } + } +catch { + } + + + +try { + Get-ScheduledTask -TaskName "One Hour Warning Message" -ErrorAction Stop | Out-Null + Unregister-ScheduledTask -TaskName "One Hour Warning Message" -Confirm:$false + } +catch { + } + +$action = New-ScheduledTaskAction -Execute 'C:\WINDOWS\system32\WindowsPowerShell\v1.0\powershell.exe' -Argument '-executionpolicy bypass -windowstyle hidden -file %programdata%\ParsecLoader\WarningMessage.ps1' + +$trigger = New-ScheduledTaskTrigger -AtLogOn -User $env:USERNAME + +Register-ScheduledTask -Action $action -Trigger $trigger -TaskName "One Hour Warning Message" -Description "This will warn you 5 minutes before you're billed for another hour" -RunLevel Highest + +Write-Output "Successfully Created" + +pause \ No newline at end of file diff --git a/application-workloads/azure-gamedev/gamedev-vm/scripts/parsec/ForceCloseShutDown.reg b/application-workloads/azure-gamedev/gamedev-vm/scripts/parsec/ForceCloseShutDown.reg new file mode 100644 index 000000000000..347c14fbd225 --- /dev/null +++ b/application-workloads/azure-gamedev/gamedev-vm/scripts/parsec/ForceCloseShutDown.reg @@ -0,0 +1,50 @@ +Windows Registry Editor Version 5.00 + +[HKEY_USERS\S-1-5-21-2402485384-1523249476-2267272849-1000\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer] +"NoLowDiskSpaceChecks"=dword:00000001 +"LinkResolveIgnoreLinkInfo"=dword:00000001 +"NoResolveSearch"=dword:00000001 +"NoResolveTrack"=dword:00000001 +"NoInternetOpenWith"=dword:00000001 + +[HKEY_USERS\S-1-5-21-2402485384-1523249476-2267272849-1000\Control Panel\Desktop] +"AutoEndTasks"="1" +"HungAppTimeout"="1000" +"MenuShowDelay"="0" +"WaitToKillAppTimeout"="2000" +"LowLevelHooksTimeout"="1000" + +[HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer] +"NoLowDiskSpaceChecks"=dword:00000001 +"LinkResolveIgnoreLinkInfo"=dword:00000001 +"NoResolveSearch"=dword:00000001 +"NoResolveTrack"=dword:00000001 +"NoInternetOpenWith"=dword:00000001 + +[HKEY_CURRENT_USER\Control Panel\Desktop] +"AutoEndTasks"="1" +"HungAppTimeout"="1000" +"MenuShowDelay"="0" +"WaitToKillAppTimeout"="2000" +"LowLevelHooksTimeout"="1000" + +[HKEY_LOCAL_MACHINE\SYSTEM\ControlSet002\Control] +"WaitToKillServiceTimeout"="1000" + +[HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control] +"WaitToKillServiceTimeout"="1000" + +[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control] +"WaitToKillServiceTimeout"="1000" + +[HKEY_USERS\S-1-5-19\Control Panel\Desktop] +"AutoEndTasks"="1" +"HungAppTimeout"="1000" +"MenuShowDelay"="0" +"WaitToKillAppTimeout"="2000" + +[HKEY_USERS\S-1-5-20\Control Panel\Desktop] +"AutoEndTasks"="1" +"HungAppTimeout"="1000" +"MenuShowDelay"="0" +"WaitToKillAppTimeout"="2000" \ No newline at end of file diff --git a/application-workloads/azure-gamedev/gamedev-vm/scripts/parsec/GPU-Update.ico b/application-workloads/azure-gamedev/gamedev-vm/scripts/parsec/GPU-Update.ico new file mode 100644 index 0000000000000000000000000000000000000000..279482c9cb310e54c4c65deca1a9b907c8f96b22 GIT binary patch literal 50762 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 literal 0 HcmV?d00001 diff --git a/application-workloads/azure-gamedev/gamedev-vm/scripts/parsec/NetworkRestore.ps1 b/application-workloads/azure-gamedev/gamedev-vm/scripts/parsec/NetworkRestore.ps1 new file mode 100644 index 000000000000..c7f52e899b5d --- /dev/null +++ b/application-workloads/azure-gamedev/gamedev-vm/scripts/parsec/NetworkRestore.ps1 @@ -0,0 +1,70 @@ +function Remove-Razer-Startup { +if (((Get-Item -Path HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Run).GetValue("Razer Synapse") -ne $null) -eq $true) +{Remove-ItemProperty -path "HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Run" -Name "Razer Synapse" +"Removed Startup Item from Razer Synapse"} +Else {"Razer Startup Item not present"} +} +Remove-Razer-Startup +function checkGPUstatus { +$getdisabled = Get-WmiObject win32_videocontroller | Where-Object {$_.name -like '*NVIDIA*' -and $_.status -like 'Error'} | Select-Object -ExpandProperty PNPDeviceID +if ($getdisabled -ne $null) {"Enabling GPU" +$var = $getdisabled.Substring(0,21) +$arguement = "/r enable"+ ' ' + "*"+ "$var"+ "*" +Start-Process -FilePath "C:\ParsecTemp\Apps\devcon.exe" -ArgumentList $arguement +} +Else {"Device is enabled"} +} +function DriverInstallStatus { +$checkdevicedriver = Get-WmiObject win32_videocontroller | Where-Object {$_.PNPDeviceID -like '*VEN_10DE*'} +if ($checkdevicedriver.name -eq "Microsoft Basic Display Adapter") {Write-output "Driver not installed" +} +Else {checkGPUStatus} +} +DriverInstallStatus + + +function check-nvidia { +$nvidiasmiarg = "-i 0 --query-gpu=driver_model.current --format=csv,noheader" +$nvidiasmidir = "c:\program files\nvidia corporation\nvsmi\nvidia-smi" +$nvidiasmiresult = Invoke-Expression "& `"$nvidiasmidir`" $nvidiasmiarg" +$nvidiadriverstatus = if($nvidiasmiresult -eq "WDDM") +{"GPU Driver status is good" +} +ElseIf($nvidiasmiresult -eq "TCC") +{Write-Output "The GPU has incorrect mode TCC set - setting WDDM" +$nvidiasmiwddm = "-g 0 -dm 0" +$nvidiasmidir = "c:\program files\nvidia corporation\nvsmi\nvidia-smi" +Invoke-Expression "& `"$nvidiasmidir`" $nvidiasmiwddm"} +Else{} +$nvidiadriverstatus} + +check-nvidia + +#set ip and dns to dhcp +function set-dhcp +{ +$global:interfaceindex = Get-NetRoute -DestinationPrefix "0.0.0.0/0" | Select-Object ifindex -ExpandProperty ifindex +$Global:interfacename = Get-NetIPInterface -InterfaceIndex $interfaceindex -AddressFamily IPv4 | select interfacealias -ExpandProperty interfacealias +$Global:setdhcp = "netsh interface ip set address '$interfacename' dhcp" +$Global:setdnsdhcp = "netsh interface ip set dns '$interfacename' dhcp" +Invoke-expression -command "$setdhcp" +Invoke-expression -command "$setdnsdhcp" +} +#enable adapter if required +function Enable-Adapter +{ +Get-NetAdapter |? status -NE Enabled | Enable-NetAdapter +} +$getdisabledadapters = Get-NetAdapter |? status -ne enabled +#query device and perform required fix +$networkadapterstatus = if($getdisabledadapters -ne $null) +{"no adapter found - enabling disabled adapters and setting dhcp" +enable-adapter +set-dhcp +} +else +{"Resetting DHCP" +set-dhcp +} + +$networkadapterstatus diff --git a/application-workloads/azure-gamedev/gamedev-vm/scripts/parsec/NetworkRestore.reg b/application-workloads/azure-gamedev/gamedev-vm/scripts/parsec/NetworkRestore.reg new file mode 100644 index 0000000000000000000000000000000000000000..9646cbb88f07d440c0d5eb74b0fda23ce69dcc30 GIT binary patch literal 1726 zcmdUv-Ae*N6vfYT(ElL&R8WycFF_AsS(0C9B+-VLS}ys;x}yH`)$h)(Uxfrg4>Ify zGxyG!JNKM>KR;>;)K^ObO*B$U6}9!o-bj%`&Dgod8n7C$PU!)Ah&5o{XMLt49W!>d zW(vysw=OOGP-20tH7MWmQxX{uPU~z?p0&I;&uvPHo+c%KF}*S`dC+^^FbKn z5fVcm`+6+CZrtgyhQw6{qw5f7?lwaXutcCgZ&kFGs}Apu5|;W~AHR=YVh%hwE2D%% zba5q57kqh`YpMZzvCVUAc}_8f4W4tKEF5=HM-}CcKyHw=PJ)}!IZF7cDa)^UN1W^F zR42}Bsik!#`6#(Td>yCEHrS?okLaythTPr&t;oLz-uD-!HfnT^6TkWI=gYjN>gp&~ zdkNjQh$M8ew5W2c;}*HFs$Gusme+)7YE@UOIfz*(l7%RF|GVpR#<^vqO%EHwUV6c% zDJM)gG+kK7i&5r0<=8Q0OcHB{xyvxG-?Hq{@22=S>3_jOCx$T9o?f({uyf$}`H%lL Rb!)57$ng=;1&1r$?+e|)9%=vp literal 0 HcmV?d00001 diff --git a/application-workloads/azure-gamedev/gamedev-vm/scripts/parsec/OneHour.ps1 b/application-workloads/azure-gamedev/gamedev-vm/scripts/parsec/OneHour.ps1 new file mode 100644 index 000000000000..45475fb3fd69 --- /dev/null +++ b/application-workloads/azure-gamedev/gamedev-vm/scripts/parsec/OneHour.ps1 @@ -0,0 +1,17 @@ +function OneHour { +Write-Output "Launched" | Out-File C:\ParsecTemp\Launched.txt +$Seconds = Get-Content $env:programdata\ParsecLoader\Time.txt +$Count = 0 +do { +$Count++ +Start-Sleep -s 1 +$Count +} +Until($Count -ge $Seconds) + + +Start-Process powershell.exe -ArgumentList "-windowstyle hidden -executionpolicy bypass -file $env:programdata\ParsecLoader\ShowDialog.ps1" + +} + +OneHour \ No newline at end of file diff --git a/application-workloads/azure-gamedev/gamedev-vm/scripts/parsec/Parsec.png b/application-workloads/azure-gamedev/gamedev-vm/scripts/parsec/Parsec.png new file mode 100644 index 0000000000000000000000000000000000000000..f0d926a96a45fe0f342d1086b698d4415d2a3261 GIT binary patch literal 6702 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 From 65585bd6c2d8130c710fb6d64eb854ef48c40f8d Mon Sep 17 00:00:00 2001 From: Daniel Ciborowski Date: Tue, 26 Apr 2022 16:42:09 +0000 Subject: [PATCH 62/71] simplify nic --- .../azure-gamedev/gamedev-vm/azuredeploy.json | 107 +++++++----------- .../nestedtemplates/gamedev-vm.bicep | 33 +++--- 2 files changed, 52 insertions(+), 88 deletions(-) diff --git a/application-workloads/azure-gamedev/gamedev-vm/azuredeploy.json b/application-workloads/azure-gamedev/gamedev-vm/azuredeploy.json index 189b0a1d2f70..382723ab17ea 100644 --- a/application-workloads/azure-gamedev/gamedev-vm/azuredeploy.json +++ b/application-workloads/azure-gamedev/gamedev-vm/azuredeploy.json @@ -5,7 +5,7 @@ "_generator": { "name": "bicep", "version": "0.5.6.12127", - "templateHash": "14072181801581721908" + "templateHash": "532371268511934668" } }, "parameters": { @@ -142,7 +142,7 @@ "_generator": { "name": "bicep", "version": "0.5.6.12127", - "templateHash": "18412523657805442082" + "templateHash": "2223702273171462724" } }, "parameters": { @@ -423,8 +423,13 @@ "publicIpNewOrExisting": { "type": "string", "defaultValue": "new", + "allowedValues": [ + "new", + "existing", + "none" + ], "metadata": { - "description": "Public IP New or Existing or None?" + "description": "Public IP With or None?" } }, "publicIpRGName": { @@ -539,12 +544,9 @@ "Script2Run": "TeradiciRegCAS.ps1", "CSEParams": "[format(' -pcoip_registration_code {0}', parameters('teradiciRegKey'))]", "cmdTeradiciRegistration": "[format('./{0}{1}', variables('Script2Run'), variables('CSEParams'))]", - "vnetId": { - "new": "[resourceId('Microsoft.Network/virtualNetworks', parameters('vnetName'))]", - "existing": "[resourceId(parameters('vnetRGName'), 'Microsoft.Network/virtualNetworks', parameters('vnetName'))]" - }, - "subnetId": "[format('{0}/subnets/{1}', variables('vnetId')[parameters('vnetNewOrExisting')], parameters('subNetName'))]", - "publicIpId": "[createObject('new', resourceId('Microsoft.Network/publicIPAddresses', parameters('publicIpName')), 'existing', resourceId(parameters('publicIpRGName'), 'Microsoft.Network/publicIPAddresses', parameters('publicIpName')), 'none', '')[parameters('publicIpNewOrExisting')]]" + "vnetId": "[resourceId(parameters('vnetRGName'), 'Microsoft.Network/virtualNetworks', parameters('vnetName'))]", + "subnetId": "[format('{0}/subnets/{1}', variables('vnetId'), parameters('subNetName'))]", + "publicIpId": "[if(equals(parameters('publicIpNewOrExisting'), 'none'), '', resourceId(parameters('publicIpRGName'), 'Microsoft.Network/publicIPAddresses', parameters('publicIpName')))]" }, "resources": [ { @@ -565,10 +567,10 @@ "type": "Microsoft.Network/publicIPAddresses", "apiVersion": "2021-05-01", "name": "[parameters('publicIpName')]", + "location": "[parameters('location')]", "sku": { "name": "[parameters('publicIpSku')]" }, - "location": "[parameters('location')]", "properties": { "publicIPAllocationMethod": "[parameters('publicIpAllocationMethod')]", "dnsSettings": { @@ -582,11 +584,8 @@ "name": "[variables('nsgName')]", "location": "[parameters('location')]", "properties": { - "securityRules": "[reference(resourceId('Microsoft.Resources/deployments', 'nsg_rules')).outputs.nsgRules.value[format('nsgRules-{0}', parameters('remoteAccessTechnology'))]]" - }, - "dependsOn": [ - "[resourceId('Microsoft.Resources/deployments', 'nsg_rules')]" - ] + "securityRules": "[createObject('nsgRules-RDP', if(not(parameters('unrealPixelStreamingEnabled')), createArray(createObject('name', 'RDP', 'properties', createObject('priority', 1010, 'protocol', '*', 'access', 'Allow', 'direction', 'Inbound', 'sourceAddressPrefix', '*', 'sourcePortRange', '*', 'destinationAddressPrefix', '*', 'destinationPortRange', '3389'))), createArray(createObject('name', 'RDP', 'properties', createObject('priority', 1010, 'protocol', '*', 'access', 'Allow', 'direction', 'Inbound', 'sourceAddressPrefix', '*', 'sourcePortRange', '*', 'destinationAddressPrefix', '*', 'destinationPortRange', '3389')), createObject('name', 'PixelStream', 'properties', createObject('priority', 1020, 'protocol', '*', 'access', 'Allow', 'direction', 'Inbound', 'sourceAddressPrefix', '*', 'sourcePortRange', '*', 'destinationAddressPrefix', '*', 'destinationPortRange', '80')))), 'nsgRules-Teradici', if(not(parameters('unrealPixelStreamingEnabled')), createArray(createObject('name', 'RDP', 'properties', createObject('priority', 1010, 'protocol', '*', 'access', 'Allow', 'direction', 'Inbound', 'sourceAddressPrefix', '*', 'sourcePortRange', '*', 'destinationAddressPrefix', '*', 'destinationPortRange', '3389')), createObject('name', 'PCoIPtcp', 'properties', createObject('priority', 1020, 'protocol', 'TCP', 'access', 'Allow', 'direction', 'Inbound', 'sourceAddressPrefix', '*', 'sourcePortRange', '*', 'destinationAddressPrefix', '*', 'destinationPortRange', '4172')), createObject('name', 'PCoIPudp', 'properties', createObject('priority', 1030, 'protocol', 'UDP', 'access', 'Allow', 'direction', 'Inbound', 'sourceAddressPrefix', '*', 'sourcePortRange', '*', 'destinationAddressPrefix', '*', 'destinationPortRange', '4172')), createObject('name', 'CertAuthHTTPS', 'properties', createObject('priority', 1040, 'protocol', 'TCP', 'access', 'Allow', 'direction', 'Inbound', 'sourceAddressPrefix', '*', 'sourcePortRange', '*', 'destinationAddressPrefix', '*', 'destinationPortRange', '443')), createObject('name', 'TeradiciCom', 'properties', createObject('priority', 1050, 'protocol', 'TCP', 'access', 'Allow', 'direction', 'Inbound', 'sourceAddressPrefix', '*', 'sourcePortRange', '*', 'destinationAddressPrefix', '*', 'destinationPortRange', '60443'))), createArray(createObject('name', 'RDP', 'properties', createObject('priority', 1010, 'protocol', '*', 'access', 'Allow', 'direction', 'Inbound', 'sourceAddressPrefix', '*', 'sourcePortRange', '*', 'destinationAddressPrefix', '*', 'destinationPortRange', '3389')), createObject('name', 'PCoIPtcp', 'properties', createObject('priority', 1020, 'protocol', 'TCP', 'access', 'Allow', 'direction', 'Inbound', 'sourceAddressPrefix', '*', 'sourcePortRange', '*', 'destinationAddressPrefix', '*', 'destinationPortRange', '4172')), createObject('name', 'PCoIPudp', 'properties', createObject('priority', 1030, 'protocol', 'UDP', 'access', 'Allow', 'direction', 'Inbound', 'sourceAddressPrefix', '*', 'sourcePortRange', '*', 'destinationAddressPrefix', '*', 'destinationPortRange', '4172')), createObject('name', 'CertAuthHTTPS', 'properties', createObject('priority', 1040, 'protocol', 'TCP', 'access', 'Allow', 'direction', 'Inbound', 'sourceAddressPrefix', '*', 'sourcePortRange', '*', 'destinationAddressPrefix', '*', 'destinationPortRange', '443')), createObject('name', 'TeradiciCom', 'properties', createObject('priority', 1050, 'protocol', 'TCP', 'access', 'Allow', 'direction', 'Inbound', 'sourceAddressPrefix', '*', 'sourcePortRange', '*', 'destinationAddressPrefix', '*', 'destinationPortRange', '60443')), createObject('name', 'PixelStream', 'properties', createObject('priority', 1060, 'protocol', '*', 'access', 'Allow', 'direction', 'Inbound', 'sourceAddressPrefix', '*', 'sourcePortRange', '*', 'destinationAddressPrefix', '*', 'destinationPortRange', '80')))), 'nsgRules-Parsec', if(not(parameters('unrealPixelStreamingEnabled')), createArray(createObject('name', 'RDP', 'properties', createObject('priority', 1010, 'protocol', '*', 'access', 'Allow', 'direction', 'Inbound', 'sourceAddressPrefix', '*', 'sourcePortRange', '*', 'destinationAddressPrefix', '*', 'destinationPortRange', '3389'))), createArray(createObject('name', 'RDP', 'properties', createObject('priority', 1010, 'protocol', '*', 'access', 'Allow', 'direction', 'Inbound', 'sourceAddressPrefix', '*', 'sourcePortRange', '*', 'destinationAddressPrefix', '*', 'destinationPortRange', '3389')), createObject('name', 'PixelStream', 'properties', createObject('priority', 1020, 'protocol', '*', 'access', 'Allow', 'direction', 'Inbound', 'sourceAddressPrefix', '*', 'sourcePortRange', '*', 'destinationAddressPrefix', '*', 'destinationPortRange', '80')))))[format('nsgRules-{0}', parameters('remoteAccessTechnology'))]]" + } }, { "condition": "[equals(parameters('vnetNewOrExisting'), 'new')]", @@ -620,7 +619,13 @@ "ipConfigurations": [ { "name": "[variables('ipconfName')]", - "properties": "[union(createObject('subnet', createObject('id', variables('subnetId'))), createObject('privateIPAllocationMethod', 'Dynamic'), if(not(empty(variables('publicIpId'))), createObject('publicIPAddress', createObject('id', variables('publicIpId'))), createObject()))]" + "properties": { + "subnet": { + "id": "[variables('subnetId')]" + }, + "privateIPAllocationMethod": "Dynamic", + "publicIPAddress": "[if(equals(parameters('publicIpNewOrExisting'), 'none'), null(), createObject('id', resourceId(parameters('publicIpRGName'), 'Microsoft.Network/publicIpAddresses', parameters('publicIpName'))))]" + } } ], "networkSecurityGroup": { @@ -639,6 +644,7 @@ "location": "[parameters('location')]", "plan": "[variables('vmPlan')]", "identity": "[if(or(parameters('enableAAD'), parameters('enableManagedIdentity')), createObject('type', 'systemAssigned'), null())]", + "tags": "[if(contains(parameters('outTagsByResource'), 'Microsoft.Compute/virtualMachines'), union(variables('tags'), parameters('outTagsByResource')['Microsoft.Compute/virtualMachines']), variables('tags'))]", "properties": { "hardwareProfile": { "vmSize": "[parameters('vmSize')]" @@ -680,7 +686,6 @@ ] } }, - "tags": "[if(contains(parameters('outTagsByResource'), 'Microsoft.Compute/virtualMachines'), union(variables('tags'), parameters('outTagsByResource')['Microsoft.Compute/virtualMachines']), variables('tags'))]", "dependsOn": [ "[resourceId('Microsoft.Network/networkInterfaces', variables('nicName'))]" ] @@ -702,56 +707,6 @@ "[resourceId('Microsoft.Compute/virtualMachines', parameters('vmName'))]" ] }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2020-10-01", - "name": "nsg_rules", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "addPixelStreamingPorts": { - "value": "[parameters('unrealPixelStreamingEnabled')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.5.6.12127", - "templateHash": "8432771765242177029" - } - }, - "parameters": { - "addPixelStreamingPorts": { - "type": "bool", - "defaultValue": false, - "metadata": { - "description": "Enable Pick Streaming" - } - } - }, - "variables": { - "nsgRules": { - "nsgRules-RDP": "[if(not(parameters('addPixelStreamingPorts')), createArray(createObject('name', 'RDP', 'properties', createObject('priority', 1010, 'protocol', '*', 'access', 'Allow', 'direction', 'Inbound', 'sourceAddressPrefix', '*', 'sourcePortRange', '*', 'destinationAddressPrefix', '*', 'destinationPortRange', '3389'))), createArray(createObject('name', 'RDP', 'properties', createObject('priority', 1010, 'protocol', '*', 'access', 'Allow', 'direction', 'Inbound', 'sourceAddressPrefix', '*', 'sourcePortRange', '*', 'destinationAddressPrefix', '*', 'destinationPortRange', '3389')), createObject('name', 'PixelStream', 'properties', createObject('priority', 1020, 'protocol', '*', 'access', 'Allow', 'direction', 'Inbound', 'sourceAddressPrefix', '*', 'sourcePortRange', '*', 'destinationAddressPrefix', '*', 'destinationPortRange', '80'))))]", - "nsgRules-Teradici": "[if(not(parameters('addPixelStreamingPorts')), createArray(createObject('name', 'RDP', 'properties', createObject('priority', 1010, 'protocol', '*', 'access', 'Allow', 'direction', 'Inbound', 'sourceAddressPrefix', '*', 'sourcePortRange', '*', 'destinationAddressPrefix', '*', 'destinationPortRange', '3389')), createObject('name', 'PCoIPtcp', 'properties', createObject('priority', 1020, 'protocol', 'TCP', 'access', 'Allow', 'direction', 'Inbound', 'sourceAddressPrefix', '*', 'sourcePortRange', '*', 'destinationAddressPrefix', '*', 'destinationPortRange', '4172')), createObject('name', 'PCoIPudp', 'properties', createObject('priority', 1030, 'protocol', 'UDP', 'access', 'Allow', 'direction', 'Inbound', 'sourceAddressPrefix', '*', 'sourcePortRange', '*', 'destinationAddressPrefix', '*', 'destinationPortRange', '4172')), createObject('name', 'CertAuthHTTPS', 'properties', createObject('priority', 1040, 'protocol', 'TCP', 'access', 'Allow', 'direction', 'Inbound', 'sourceAddressPrefix', '*', 'sourcePortRange', '*', 'destinationAddressPrefix', '*', 'destinationPortRange', '443')), createObject('name', 'TeradiciCom', 'properties', createObject('priority', 1050, 'protocol', 'TCP', 'access', 'Allow', 'direction', 'Inbound', 'sourceAddressPrefix', '*', 'sourcePortRange', '*', 'destinationAddressPrefix', '*', 'destinationPortRange', '60443'))), createArray(createObject('name', 'RDP', 'properties', createObject('priority', 1010, 'protocol', '*', 'access', 'Allow', 'direction', 'Inbound', 'sourceAddressPrefix', '*', 'sourcePortRange', '*', 'destinationAddressPrefix', '*', 'destinationPortRange', '3389')), createObject('name', 'PCoIPtcp', 'properties', createObject('priority', 1020, 'protocol', 'TCP', 'access', 'Allow', 'direction', 'Inbound', 'sourceAddressPrefix', '*', 'sourcePortRange', '*', 'destinationAddressPrefix', '*', 'destinationPortRange', '4172')), createObject('name', 'PCoIPudp', 'properties', createObject('priority', 1030, 'protocol', 'UDP', 'access', 'Allow', 'direction', 'Inbound', 'sourceAddressPrefix', '*', 'sourcePortRange', '*', 'destinationAddressPrefix', '*', 'destinationPortRange', '4172')), createObject('name', 'CertAuthHTTPS', 'properties', createObject('priority', 1040, 'protocol', 'TCP', 'access', 'Allow', 'direction', 'Inbound', 'sourceAddressPrefix', '*', 'sourcePortRange', '*', 'destinationAddressPrefix', '*', 'destinationPortRange', '443')), createObject('name', 'TeradiciCom', 'properties', createObject('priority', 1050, 'protocol', 'TCP', 'access', 'Allow', 'direction', 'Inbound', 'sourceAddressPrefix', '*', 'sourcePortRange', '*', 'destinationAddressPrefix', '*', 'destinationPortRange', '60443')), createObject('name', 'PixelStream', 'properties', createObject('priority', 1060, 'protocol', '*', 'access', 'Allow', 'direction', 'Inbound', 'sourceAddressPrefix', '*', 'sourcePortRange', '*', 'destinationAddressPrefix', '*', 'destinationPortRange', '80'))))]", - "nsgRules-Parsec": "[if(not(parameters('addPixelStreamingPorts')), createArray(createObject('name', 'RDP', 'properties', createObject('priority', 1010, 'protocol', '*', 'access', 'Allow', 'direction', 'Inbound', 'sourceAddressPrefix', '*', 'sourcePortRange', '*', 'destinationAddressPrefix', '*', 'destinationPortRange', '3389'))), createArray(createObject('name', 'RDP', 'properties', createObject('priority', 1010, 'protocol', '*', 'access', 'Allow', 'direction', 'Inbound', 'sourceAddressPrefix', '*', 'sourcePortRange', '*', 'destinationAddressPrefix', '*', 'destinationPortRange', '3389')), createObject('name', 'PixelStream', 'properties', createObject('priority', 1020, 'protocol', '*', 'access', 'Allow', 'direction', 'Inbound', 'sourceAddressPrefix', '*', 'sourcePortRange', '*', 'destinationAddressPrefix', '*', 'destinationPortRange', '80'))))]" - } - }, - "resources": [], - "outputs": { - "nsgRules": { - "type": "object", - "value": "[variables('nsgRules')]" - } - } - } - } - }, { "type": "Microsoft.Resources/deployments", "apiVersion": "2020-10-01", @@ -818,7 +773,7 @@ "_generator": { "name": "bicep", "version": "0.5.6.12127", - "templateHash": "10609471965789775318" + "templateHash": "4414641532926436161" } }, "parameters": { @@ -975,7 +930,21 @@ "[uri(parameters('_artifactsLocation'), format('scripts/p4DepotSync.ps1{0}', parameters('_artifactsLocationSasToken')))]", "[uri(parameters('_artifactsLocation'), format('scripts/ibSetup.ps1{0}', parameters('_artifactsLocationSasToken')))]", "[uri(parameters('_artifactsLocation'), format('scripts/PostInstall.ps1{0}', parameters('_artifactsLocationSasToken')))]", - "[uri(parameters('_artifactsLocation'), format('scripts/PreInstall.zip{0}', parameters('_artifactsLocationSasToken')))]" + "[uri(parameters('_artifactsLocation'), format('scripts/parsec/Automatic-Shutdown.ps1{0}', parameters('_artifactsLocationSasToken')))]", + "[uri(parameters('_artifactsLocation'), format('scripts/parsec/Clear-Proxy.ps1{0}', parameters('_artifactsLocationSasToken')))]", + "[uri(parameters('_artifactsLocation'), format('scripts/parsec/CreateAutomaticShutdownScheduledTask.ps1{0}', parameters('_artifactsLocationSasToken')))]", + "[uri(parameters('_artifactsLocation'), format('scripts/parsec/CreateClearProxyScheduledTask.ps1{0}', parameters('_artifactsLocationSasToken')))]", + "[uri(parameters('_artifactsLocation'), format('scripts/parsec/ForceCloseShutDown.reg{0}', parameters('_artifactsLocationSasToken')))]", + "[uri(parameters('_artifactsLocation'), format('scripts/parsec/gpt.ini{0}', parameters('_artifactsLocationSasToken')))]", + "[uri(parameters('_artifactsLocation'), format('scripts/parsec/GPU-Update.ico{0}', parameters('_artifactsLocationSasToken')))]", + "[uri(parameters('_artifactsLocation'), format('scripts/parsec/NetWorkRestore.ps1{0}', parameters('_artifactsLocationSasToken')))]", + "[uri(parameters('_artifactsLocation'), format('scripts/parsec/NetworkRestore.reg{0}', parameters('_artifactsLocationSasToken')))]", + "[uri(parameters('_artifactsLocation'), format('scripts/parsec/OneHour.ps1{0}', parameters('_artifactsLocationSasToken')))]", + "[uri(parameters('_artifactsLocation'), format('scripts/parsec/Parsec.png{0}', parameters('_artifactsLocationSasToken')))]", + "[uri(parameters('_artifactsLocation'), format('scripts/parsec/psscripts.ini{0}', parameters('_artifactsLocationSasToken')))]", + "[uri(parameters('_artifactsLocation'), format('scripts/parsec/ShowDialog.ps1{0}', parameters('_artifactsLocationSasToken')))]", + "[uri(parameters('_artifactsLocation'), format('scripts/parsec/TeamMachineSetup.ps1{0}', parameters('_artifactsLocationSasToken')))]", + "[uri(parameters('_artifactsLocation'), format('scripts/parsec/WarningMessage.ps1{0}', parameters('_artifactsLocationSasToken')))]" ], "commandToExecute": "[format('powershell -ExecutionPolicy Unrestricted -NoProfile -NonInteractive -command \"./CreateDataDisk.ps1;./MountFileShare.ps1 {0};./p4DepotSync.ps1 {1};./ibSetup.ps1;{2};./PostInstall.ps1\"', variables('mountFileShareParams'), variables('p4Params'), parameters('cmdGDKInstall'))]" } diff --git a/application-workloads/azure-gamedev/gamedev-vm/nestedtemplates/gamedev-vm.bicep b/application-workloads/azure-gamedev/gamedev-vm/nestedtemplates/gamedev-vm.bicep index e0fdf98fff8d..64984a531776 100644 --- a/application-workloads/azure-gamedev/gamedev-vm/nestedtemplates/gamedev-vm.bicep +++ b/application-workloads/azure-gamedev/gamedev-vm/nestedtemplates/gamedev-vm.bicep @@ -518,24 +518,6 @@ resource vnet 'Microsoft.Network/virtualNetworks@2021-05-01' = if (vnetNewOrExis } } -var publicIpProperties = (!empty(publicIpId)) ? { - publicIPAddress: { - id: publicIpId - } -} : {} - -var ipConfigurations = [ - { - name: ipconfName - properties: union(publicIpProperties, { - subnet: { - id: subnetId - } - privateIPAllocationMethod: 'Dynamic' - }) - } -] - resource nic 'Microsoft.Network/networkInterfaces@2021-05-01' = { name: nicName location: location @@ -544,7 +526,20 @@ resource nic 'Microsoft.Network/networkInterfaces@2021-05-01' = { ] properties: { enableAcceleratedNetworking: (bool(length(split(vmSize, '_')) > 2) ? true : false) - ipConfigurations: ipConfigurations + ipConfigurations: [ + { + name: ipconfName + properties: { + subnet: { + id: subnetId + } + privateIPAllocationMethod: 'Dynamic' + publicIPAddress: publicIpNewOrExisting == 'none' ? null: { + id: resourceId(publicIpRGName, 'Microsoft.Network/publicIpAddresses', publicIpName) + } + } + } + ] networkSecurityGroup: { id: nsg.id } From d977aa0fb1950b303062607edd0ab752529eba38 Mon Sep 17 00:00:00 2001 From: Daniel Ciborowski Date: Tue, 26 Apr 2022 16:43:12 +0000 Subject: [PATCH 63/71] simplify nic --- .../gamedev-vm/nestedtemplates/gamedev-vm.bicep | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/application-workloads/azure-gamedev/gamedev-vm/nestedtemplates/gamedev-vm.bicep b/application-workloads/azure-gamedev/gamedev-vm/nestedtemplates/gamedev-vm.bicep index 64984a531776..d0f3aae10398 100644 --- a/application-workloads/azure-gamedev/gamedev-vm/nestedtemplates/gamedev-vm.bicep +++ b/application-workloads/azure-gamedev/gamedev-vm/nestedtemplates/gamedev-vm.bicep @@ -228,11 +228,6 @@ var Script2Run = 'TeradiciRegCAS.ps1' var CSEParams = ' -pcoip_registration_code ${teradiciRegKey}' var cmdTeradiciRegistration = './${Script2Run}${CSEParams}' -var vnetId = resourceId(vnetRGName, 'Microsoft.Network/virtualNetworks', vnetName) -var subnetId = '${vnetId}/subnets/${subNetName}' - -var publicIpId = publicIpNewOrExisting == 'none' ? '' : resourceId(publicIpRGName, 'Microsoft.Network/publicIPAddresses', publicIpName) - resource partnercenter 'Microsoft.Resources/deployments@2021-04-01' = { name: 'pid-7837dd60-4ba8-419a-a26f-237bbe170773-partnercenter' properties: { @@ -531,7 +526,7 @@ resource nic 'Microsoft.Network/networkInterfaces@2021-05-01' = { name: ipconfName properties: { subnet: { - id: subnetId + id: '${resourceId(vnetRGName, 'Microsoft.Network/virtualNetworks', vnetName)}/subnets/${subNetName}' } privateIPAllocationMethod: 'Dynamic' publicIPAddress: publicIpNewOrExisting == 'none' ? null: { @@ -626,7 +621,3 @@ resource virtualMachine_enableAAD 'Microsoft.Compute/virtualMachines/extensions@ autoUpgradeMinorVersion: true } } - -output Host_Name string = (!empty(publicIpId) ? reference(publicIpId, '2021-03-01').dnsSettings.fqdn : '') -output UserName string = adminName -output IPAddress string = (!empty(publicIpId) ? publicIpId : '') From 634c6380b23cc53b70a15b868a50aedaa9728237 Mon Sep 17 00:00:00 2001 From: Daniel Ciborowski Date: Tue, 26 Apr 2022 16:46:49 +0000 Subject: [PATCH 64/71] make secure string param --- .../azure-gamedev/gamedev-vm/scripts/MountFileShare.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application-workloads/azure-gamedev/gamedev-vm/scripts/MountFileShare.ps1 b/application-workloads/azure-gamedev/gamedev-vm/scripts/MountFileShare.ps1 index a91005d36957..67b6c2b0dae9 100644 --- a/application-workloads/azure-gamedev/gamedev-vm/scripts/MountFileShare.ps1 +++ b/application-workloads/azure-gamedev/gamedev-vm/scripts/MountFileShare.ps1 @@ -1,6 +1,6 @@ param ( [string]$storageAccount, - [string]$storageAccountKey, + [SecureString]$storageAccountKey, [string]$fileShareName) if ($storageAccount -and $storageAccountKey -and $fileShareName) From 93c360674aaf2a175e6a35326174afc2d941c99e Mon Sep 17 00:00:00 2001 From: Daniel Ciborowski Date: Tue, 26 Apr 2022 16:53:56 +0000 Subject: [PATCH 65/71] harden nvidia driver code --- .../gamedev-vm/scripts/parsec/Clear-Proxy.ps1 | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/application-workloads/azure-gamedev/gamedev-vm/scripts/parsec/Clear-Proxy.ps1 b/application-workloads/azure-gamedev/gamedev-vm/scripts/parsec/Clear-Proxy.ps1 index 58eda36cae31..c2310faf0d2e 100644 --- a/application-workloads/azure-gamedev/gamedev-vm/scripts/parsec/Clear-Proxy.ps1 +++ b/application-workloads/azure-gamedev/gamedev-vm/scripts/parsec/Clear-Proxy.ps1 @@ -194,10 +194,20 @@ if (($gpu.supported -eq "UnOfficial") -eq $true) { (New-Object System.Net.WebClient).DownloadFile($($($URL.GoogleGRID).links | Where-Object href -like *server2016_64bit_international.exe*).href, "C:\ParsecTemp\Drivers\GoogleGRID.exe") } Else { -#downloads driver from nvidia.com -$Download.Link = Invoke-WebRequest -Uri $gpu.url -Method Get -UseBasicParsing | select @{N='Latest';E={$($_.links.href -match"www.nvidia.com/download/driverResults.aspx*")[0].substring(2)}} -$download.Direct = Invoke-WebRequest -Uri $download.link.latest -Method Get -UseBasicParsing | select @{N= 'Download'; E={"http://us.download.nvidia.com" + $($_.links.href -match "/content/driverdownload*").split('=')[1].split('&')[0]}} -(New-Object System.Net.WebClient).DownloadFile($($download.direct.download), $($system.Path) + "\NVIDIA_" + $($gpu.web_driver) + ".exe") +# Downloads driver from nvidia.com per https://github.com/lord-carlos/nvidia-update/blob/master/nvidia.ps1 +# Checking latest driver version from Nvidia website +$link = Invoke-WebRequest -Uri 'https://www.nvidia.com/Download/processFind.aspx?psid=101&pfid=816&osid=57&lid=1&whql=1&lang=en-us&ctk=0&dtcid=1' -Method GET -UseBasicParsing +$link -match '([^<]+?)' | Out-Null +$version = $matches[1] +Write-Host "Latest version `t`t$version" + +# Generating the download link +$url = "https://international.download.nvidia.com/Windows/$version/$version-desktop-$windowsVersion-$windowsArchitecture-international-dch-whql.exe" + +# Downloading the installer +$dlFile = $($system.Path) + "\NVIDIA_" + $($gpu.web_driver) + ".exe" +Write-Host "Downloading the latest version to $dlFile" +Start-BitsTransfer -Source $url -Destination $dlFile } } From f263812a978ff99a5393040b005f4483bba15f6e Mon Sep 17 00:00:00 2001 From: Daniel Ciborowski Date: Tue, 26 Apr 2022 16:55:36 +0000 Subject: [PATCH 66/71] update arm template --- .../azure-gamedev/gamedev-vm/azuredeploy.json | 27 ++++--------------- 1 file changed, 5 insertions(+), 22 deletions(-) diff --git a/application-workloads/azure-gamedev/gamedev-vm/azuredeploy.json b/application-workloads/azure-gamedev/gamedev-vm/azuredeploy.json index 382723ab17ea..7f161e58f6c8 100644 --- a/application-workloads/azure-gamedev/gamedev-vm/azuredeploy.json +++ b/application-workloads/azure-gamedev/gamedev-vm/azuredeploy.json @@ -5,7 +5,7 @@ "_generator": { "name": "bicep", "version": "0.5.6.12127", - "templateHash": "532371268511934668" + "templateHash": "51586525294520461" } }, "parameters": { @@ -142,7 +142,7 @@ "_generator": { "name": "bicep", "version": "0.5.6.12127", - "templateHash": "2223702273171462724" + "templateHash": "12534744390091869204" } }, "parameters": { @@ -543,10 +543,7 @@ "userData": "[format('team_id={0}:key={1}:name={2}:user_email={3}:is_guest_access={4}:ibLicenseKey={5}', parameters('parsec_teamId'), parameters('parsec_teamKey'), parameters('parsec_host'), parameters('parsec_userEmail'), parameters('parsec_isGuestAccess'), parameters('ibLicenseKey'))]", "Script2Run": "TeradiciRegCAS.ps1", "CSEParams": "[format(' -pcoip_registration_code {0}', parameters('teradiciRegKey'))]", - "cmdTeradiciRegistration": "[format('./{0}{1}', variables('Script2Run'), variables('CSEParams'))]", - "vnetId": "[resourceId(parameters('vnetRGName'), 'Microsoft.Network/virtualNetworks', parameters('vnetName'))]", - "subnetId": "[format('{0}/subnets/{1}', variables('vnetId'), parameters('subNetName'))]", - "publicIpId": "[if(equals(parameters('publicIpNewOrExisting'), 'none'), '', resourceId(parameters('publicIpRGName'), 'Microsoft.Network/publicIPAddresses', parameters('publicIpName')))]" + "cmdTeradiciRegistration": "[format('./{0}{1}', variables('Script2Run'), variables('CSEParams'))]" }, "resources": [ { @@ -621,7 +618,7 @@ "name": "[variables('ipconfName')]", "properties": { "subnet": { - "id": "[variables('subnetId')]" + "id": "[format('{0}/subnets/{1}', resourceId(parameters('vnetRGName'), 'Microsoft.Network/virtualNetworks', parameters('vnetName')), parameters('subNetName'))]" }, "privateIPAllocationMethod": "Dynamic", "publicIPAddress": "[if(equals(parameters('publicIpNewOrExisting'), 'none'), null(), createObject('id', resourceId(parameters('publicIpRGName'), 'Microsoft.Network/publicIpAddresses', parameters('publicIpName'))))]" @@ -972,21 +969,7 @@ "[resourceId('Microsoft.Compute/virtualMachines', parameters('vmName'))]" ] } - ], - "outputs": { - "Host_Name": { - "type": "string", - "value": "[if(not(empty(variables('publicIpId'))), reference(variables('publicIpId'), '2021-03-01').dnsSettings.fqdn, '')]" - }, - "UserName": { - "type": "string", - "value": "[parameters('adminName')]" - }, - "IPAddress": { - "type": "string", - "value": "[if(not(empty(variables('publicIpId'))), variables('publicIpId'), '')]" - } - } + ] } } } From d3ff7d12bce36036662fab0b65c67e268018eba7 Mon Sep 17 00:00:00 2001 From: Daniel Ciborowski Date: Wed, 27 Apr 2022 13:19:13 -0400 Subject: [PATCH 67/71] Update application-workloads/azure-gamedev/gamedev-vm/nestedtemplates/gamedev-vm.bicep Co-authored-by: Brian Moore --- .../azure-gamedev/gamedev-vm/nestedtemplates/gamedev-vm.bicep | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application-workloads/azure-gamedev/gamedev-vm/nestedtemplates/gamedev-vm.bicep b/application-workloads/azure-gamedev/gamedev-vm/nestedtemplates/gamedev-vm.bicep index d0f3aae10398..f5ecb89ec825 100644 --- a/application-workloads/azure-gamedev/gamedev-vm/nestedtemplates/gamedev-vm.bicep +++ b/application-workloads/azure-gamedev/gamedev-vm/nestedtemplates/gamedev-vm.bicep @@ -526,7 +526,7 @@ resource nic 'Microsoft.Network/networkInterfaces@2021-05-01' = { name: ipconfName properties: { subnet: { - id: '${resourceId(vnetRGName, 'Microsoft.Network/virtualNetworks', vnetName)}/subnets/${subNetName}' + id: resourceId(vnetRGName, 'Microsoft.Network/virtualNetworks/subnets', vnetName, subNetName) } privateIPAllocationMethod: 'Dynamic' publicIPAddress: publicIpNewOrExisting == 'none' ? null: { From 1e91e3fe55e1156c2b901c1b2ff16499565c8657 Mon Sep 17 00:00:00 2001 From: Daniel Ciborowski Date: Wed, 27 Apr 2022 13:27:08 -0400 Subject: [PATCH 68/71] Update application-workloads/azure-gamedev/gamedev-vm/scripts/PostInstall.ps1 --- .../azure-gamedev/gamedev-vm/scripts/PostInstall.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application-workloads/azure-gamedev/gamedev-vm/scripts/PostInstall.ps1 b/application-workloads/azure-gamedev/gamedev-vm/scripts/PostInstall.ps1 index 0a990b8e1ebc..6f6fbcd6faf9 100644 --- a/application-workloads/azure-gamedev/gamedev-vm/scripts/PostInstall.ps1 +++ b/application-workloads/azure-gamedev/gamedev-vm/scripts/PostInstall.ps1 @@ -458,7 +458,7 @@ function create-directories { #Create ParsecTemp folder in C Drive function unzip-preinstall { ProgressWriter -Status "Creating Directories (C:\ParsecTemp)" -PercentComplete $PercentComplete - 7z x ".\PreInstall.zip" "-oC:\ParsecTemp\PreInstall" -bd + Copy-Item ".\scripts" "C:\ParsecTemp\PreInstall" } #disable IE security From d7164729b1a3268df97dd83619dd50af2add0256 Mon Sep 17 00:00:00 2001 From: Daniel Ciborowski Date: Wed, 27 Apr 2022 13:31:01 -0400 Subject: [PATCH 69/71] Update PostInstall.ps1 --- .../azure-gamedev/gamedev-vm/scripts/PostInstall.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/application-workloads/azure-gamedev/gamedev-vm/scripts/PostInstall.ps1 b/application-workloads/azure-gamedev/gamedev-vm/scripts/PostInstall.ps1 index 6f6fbcd6faf9..dada2434bb2e 100644 --- a/application-workloads/azure-gamedev/gamedev-vm/scripts/PostInstall.ps1 +++ b/application-workloads/azure-gamedev/gamedev-vm/scripts/PostInstall.ps1 @@ -456,7 +456,7 @@ function create-directories { } #Create ParsecTemp folder in C Drive -function unzip-preinstall { +function prepare-parsec { ProgressWriter -Status "Creating Directories (C:\ParsecTemp)" -PercentComplete $PercentComplete Copy-Item ".\scripts" "C:\ParsecTemp\PreInstall" } @@ -974,7 +974,7 @@ Write-Host -foregroundcolor red " " #PromptUserAutoLogon -DontPromptPasswordUpdateGPU:$DontPromptPasswordUpdateGPU $ScripttaskList = @( -"unzip-preinstall"; +"prepare-parsec"; "setupEnvironment"; "addRegItems"; "create-directories"; From 37cd540a5f071219033f3cc563dea742736a8570 Mon Sep 17 00:00:00 2001 From: Daniel Ciborowski Date: Wed, 27 Apr 2022 19:21:29 -0400 Subject: [PATCH 70/71] build bicep --- .../azure-gamedev/gamedev-vm/azuredeploy.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/application-workloads/azure-gamedev/gamedev-vm/azuredeploy.json b/application-workloads/azure-gamedev/gamedev-vm/azuredeploy.json index 7f161e58f6c8..e764d3b43468 100644 --- a/application-workloads/azure-gamedev/gamedev-vm/azuredeploy.json +++ b/application-workloads/azure-gamedev/gamedev-vm/azuredeploy.json @@ -5,7 +5,7 @@ "_generator": { "name": "bicep", "version": "0.5.6.12127", - "templateHash": "51586525294520461" + "templateHash": "16021448726894426407" } }, "parameters": { @@ -142,7 +142,7 @@ "_generator": { "name": "bicep", "version": "0.5.6.12127", - "templateHash": "12534744390091869204" + "templateHash": "8508946119233764992" } }, "parameters": { @@ -618,7 +618,7 @@ "name": "[variables('ipconfName')]", "properties": { "subnet": { - "id": "[format('{0}/subnets/{1}', resourceId(parameters('vnetRGName'), 'Microsoft.Network/virtualNetworks', parameters('vnetName')), parameters('subNetName'))]" + "id": "[resourceId(parameters('vnetRGName'), 'Microsoft.Network/virtualNetworks/subnets', parameters('vnetName'), parameters('subNetName'))]" }, "privateIPAllocationMethod": "Dynamic", "publicIPAddress": "[if(equals(parameters('publicIpNewOrExisting'), 'none'), null(), createObject('id', resourceId(parameters('publicIpRGName'), 'Microsoft.Network/publicIpAddresses', parameters('publicIpName'))))]" From 5e10a771c9ec97c0899a980270af5691ae8b6eab Mon Sep 17 00:00:00 2001 From: Daniel Ciborowski Date: Thu, 28 Apr 2022 15:21:18 -0400 Subject: [PATCH 71/71] Update application-workloads/azure-gamedev/gamedev-vm/scripts/PostInstall.ps1 --- .../azure-gamedev/gamedev-vm/scripts/PostInstall.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application-workloads/azure-gamedev/gamedev-vm/scripts/PostInstall.ps1 b/application-workloads/azure-gamedev/gamedev-vm/scripts/PostInstall.ps1 index dada2434bb2e..4702250b2308 100644 --- a/application-workloads/azure-gamedev/gamedev-vm/scripts/PostInstall.ps1 +++ b/application-workloads/azure-gamedev/gamedev-vm/scripts/PostInstall.ps1 @@ -458,7 +458,7 @@ function create-directories { #Create ParsecTemp folder in C Drive function prepare-parsec { ProgressWriter -Status "Creating Directories (C:\ParsecTemp)" -PercentComplete $PercentComplete - Copy-Item ".\scripts" "C:\ParsecTemp\PreInstall" + Copy-Item ".\parsec" "C:\ParsecTemp\PreInstall" } #disable IE security