diff --git a/.travis.yml b/.travis.yml index d7ec5b0ca471..5c62be27cd68 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,4 +9,4 @@ node_js: sudo: false cache: directories: - - node_modules + - node_modules \ No newline at end of file diff --git a/101-batchaccount-with-storage/azuredeploy.json b/101-batchaccount-with-storage/azuredeploy.json index 53ddad3bd05d..841260979c89 100644 --- a/101-batchaccount-with-storage/azuredeploy.json +++ b/101-batchaccount-with-storage/azuredeploy.json @@ -4,6 +4,7 @@ "parameters": { "batchAccountName": { "type": "string", + "defaultValue": "[concat(toLower(uniqueString(resourceGroup().id)), 'batch')]", "metadata": { "description": "Batch Account Name" } diff --git a/101-batchaccount-with-storage/azuredeploy.parameters.json b/101-batchaccount-with-storage/azuredeploy.parameters.json index cedcacd179df..55c1a478b8b0 100644 --- a/101-batchaccount-with-storage/azuredeploy.parameters.json +++ b/101-batchaccount-with-storage/azuredeploy.parameters.json @@ -1,9 +1,5 @@ { "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#", "contentVersion": "1.0.0.0", - "parameters": { - "batchAccountName": { - "value": "GEN-UNIQUE-12" - } - } + "parameters": { } } diff --git a/101-key-vault-create/README.md b/101-key-vault-create/README.md index 9de2a302bb56..388eccfb225c 100644 --- a/101-key-vault-create/README.md +++ b/101-key-vault-create/README.md @@ -1,4 +1,4 @@ -# Create Key Vault +# Create an Azure Key Vault @@ -7,4 +7,17 @@ -This template creates a Key Vault. For more information, go to: http://azure.microsoft.com/en-us/documentation/services/key-vault/ +This template creates an Azure Key Vault and a secret stored inside the key vault. If you are new to Azure Key Vault, see: + +- [Azure Key Vault service](https://azure.microsoft.com/services/key-vault/) +- [Azure Key Vault documentation](https://docs.microsoft.com/azure/key-vault/) +- [Azure Key Vault template reference](https://docs.microsoft.com/azure/templates/microsoft.keyvault/allversions) +- [Quickstart templates](https://azure.microsoft.com/resources/templates/?resourceType=Microsoft.Keyvault) + +If you are new to the template development, see: + +- [Azure Resource Manager documentation](https://docs.microsoft.com/en-us/azure/azure-resource-manager/) +- [Use Azure Key Vault to pass secure parameter value during deployment](https://docs.microsoft.com/azure/azure-resource-manager/resource-manager-keyvault-parameter) +- [Tutorial: Integrate Azure Key Vault in Resource Manager Template deployment](https://docs.microsoft.com/azure/azure-resource-manager/resource-manager-tutorial-use-key-vault) + +Tags: Azure Key Vault, Key Vault, Secrets, Resource Manager, Resource Manager templates, ARM templates diff --git a/101-key-vault-create/azuredeploy.json b/101-key-vault-create/azuredeploy.json index 0020176192a7..5ead34421361 100644 --- a/101-key-vault-create/azuredeploy.json +++ b/101-key-vault-create/azuredeploy.json @@ -5,20 +5,60 @@ "keyVaultName": { "type": "string", "metadata": { - "description": "Name of the Vault" + "description": "Specifies the name of the key vault." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Specifies the Azure location where the key vault should be created." + } + }, + "enabledForDeployment": { + "type": "bool", + "defaultValue": false, + "allowedValues": [ + true, + false + ], + "metadata": { + "description": "Specifies whether Azure Virtual Machines are permitted to retrieve certificates stored as secrets from the key vault." + } + }, + "enabledForDiskEncryption": { + "type": "bool", + "defaultValue": false, + "allowedValues": [ + true, + false + ], + "metadata": { + "description": "Specifies whether Azure Disk Encryption is permitted to retrieve secrets from the vault and unwrap keys." + } + }, + "enabledForTemplateDeployment": { + "type": "bool", + "defaultValue": false, + "allowedValues": [ + true, + false + ], + "metadata": { + "description": "Specifies whether Azure Resource Manager is permitted to retrieve secrets from the key vault." } }, "tenantId": { "type": "string", "defaultValue": "[subscription().tenantId]", "metadata": { - "description": "Tenant Id of the subscription. Get using Get-AzureRmSubscription cmdlet or Get Subscription API" + "description": "Specifies the Azure Active Directory tenant ID that should be used for authenticating requests to the key vault. Get it by using Get-AzSubscription cmdlet." } }, "objectId": { "type": "string", "metadata": { - "description": "Object Id of the AD user. Get using Get-AzureRmADUser or Get-AzureRmADServicePrincipal cmdlets" + "description": "Specifies the object ID of a user, service principal or security group in the Azure Active Directory tenant for the vault. The object ID must be unique for the list of access policies. Get it by using Get-AzADUser or Get-AzADServicePrincipal cmdlets." } }, "keysPermissions": { @@ -27,7 +67,7 @@ "list" ], "metadata": { - "description": "Permissions to keys in the vault. Valid values are: all, create, import, update, get, list, delete, backup, restore, encrypt, decrypt, wrapkey, unwrapkey, sign, and verify." + "description": "Specifies the permissions to keys in the vault. Valid values are: all, encrypt, decrypt, wrapKey, unwrapKey, sign, verify, get, list, create, update, import, delete, backup, restore, recover, and purge." } }, "secretsPermissions": { @@ -36,7 +76,7 @@ "list" ], "metadata": { - "description": "Permissions to secrets in the vault. Valid values are: all, get, set, list, and delete." + "description": "Specifies the permissions to secrets in the vault. Valid values are: all, get, list, set, delete, backup, restore, recover, and purge." } }, "skuName": { @@ -47,47 +87,19 @@ "Premium" ], "metadata": { - "description": "SKU for the vault" - } - }, - "enableVaultForDeployment": { - "type": "bool", - "defaultValue": false, - "allowedValues": [ - true, - false - ], - "metadata": { - "description": "Specifies if the vault is enabled for a VM deployment" + "description": "Specifies whether the key vault is a standard vault or a premium vault." } }, - "enableVaultForDiskEncryption": { - "type": "bool", - "defaultValue": false, - "allowedValues": [ - true, - false - ], - "metadata": { - "description": "Specifies if the azure platform has access to the vault for enabling disk encryption scenarios." - } - }, - "enabledForTemplateDeployment": { - "type": "bool", - "defaultValue": false, - "allowedValues": [ - true, - false - ], + "secretName": { + "type": "string", "metadata": { - "description": "Specifies whether Azure Resource Manager is permitted to retrieve secrets from the key vault." + "description": "Specifies the name of the secret that you want to create." } }, - "location": { - "type": "string", - "defaultValue": "[resourceGroup().location]", + "secretValue": { + "type": "securestring", "metadata": { - "description": "Location for all resources." + "description": "Specifies the value of the secret that you want to create." } } }, @@ -95,28 +107,46 @@ { "type": "Microsoft.KeyVault/vaults", "name": "[parameters('keyVaultName')]", - "apiVersion": "2016-10-01", + "apiVersion": "2018-02-14", "location": "[parameters('location')]", "properties": { - "enabledForDeployment": "[parameters('enableVaultForDeployment')]", - "enabledForDiskEncryption": "[parameters('enableVaultForDiskEncryption')]", + "enabledForDeployment": "[parameters('enabledForDeployment')]", + "enabledForDiskEncryption": "[parameters('enabledForDiskEncryption')]", "enabledForTemplateDeployment": "[parameters('enabledForTemplateDeployment')]", "tenantId": "[parameters('tenantId')]", "accessPolicies": [ { - "tenantId": "[parameters('tenantId')]", "objectId": "[parameters('objectId')]", + "tenantId": "[parameters('tenantId')]", "permissions": { "keys": "[parameters('keysPermissions')]", - "secrets": "[parameters('secretsPermissions')]" + "secrets": "[parameters('secretsPermissions')]" } } ], "sku": { "name": "[parameters('skuName')]", "family": "A" + }, + "networkAcls": { + "value": { + "defaultAction": "Allow", + "bypass": "AzureServices" + } } } + }, + { + "type": "Microsoft.KeyVault/vaults/secrets", + "name": "[concat(parameters('keyVaultName'), '/', parameters('secretName'))]", + "apiVersion": "2018-02-14", + "location": "[parameters('location')]", + "dependsOn": [ + "[resourceId('Microsoft.KeyVault/vaults', parameters('keyVaultName'))]" + ], + "properties": { + "value": "[parameters('secretValue')]" + } } ] -} +} \ No newline at end of file diff --git a/101-key-vault-create/azuredeploy.parameters.json b/101-key-vault-create/azuredeploy.parameters.json index c403468035fc..711c11a0e262 100644 --- a/101-key-vault-create/azuredeploy.parameters.json +++ b/101-key-vault-create/azuredeploy.parameters.json @@ -6,7 +6,13 @@ "value": "GEN-UNIQUE" }, "objectId": { - "value": "GEN-AAD-OBJECTID" + "value": "GEN-AZUREAD-OBJECTID" + }, + "secretName": { + "value": "GEN-UNIQUE" + }, + "secretValue": { + "value": "GEN-UNIQUE" } } -} \ No newline at end of file +} diff --git a/101-key-vault-create/metadata.json b/101-key-vault-create/metadata.json index a8fdbdf16d8a..9fec7bee3275 100644 --- a/101-key-vault-create/metadata.json +++ b/101-key-vault-create/metadata.json @@ -1,11 +1,11 @@ { "$schema": "https://aka.ms/azure-quickstart-templates-metadata-schema#", "type": "QuickStart", - "itemDisplayName": "Create a Key Vault", - "description": "This template creates a Key Vault and assigns permissions to the supplied objectId (principal).", + "itemDisplayName": "Create an Azure Key Vault and a secret", + "description": "This template creates an Azure Key Vault and a secret.", "summary": "This template creates a Key Vault and assigns permissions to the supplied objectId (principal).", "githubUsername": "seanbamsft", - "dateUpdated": "2017-09-08" + "dateUpdated": "2019-02-14" } diff --git a/101-ubuntu-mate-desktop-vscode/azuredeploy.json b/101-ubuntu-mate-desktop-vscode/azuredeploy.json index 99ae2fac6c79..3331fd01d78d 100644 --- a/101-ubuntu-mate-desktop-vscode/azuredeploy.json +++ b/101-ubuntu-mate-desktop-vscode/azuredeploy.json @@ -55,7 +55,7 @@ "vmStorageAccountContainerName": "vhds", "imagePublisher": "Canonical", "imageOffer": "UbuntuServer", - "ubuntuOSVersion": "17.10", + "ubuntuOSVersion": "18.04-LTS", "OSDiskName": "OSDisk", "vmName": "vm", "nicName": "vmNic", diff --git a/201-alert-to-queue-with-logic-app/azuredeploy.json b/201-alert-to-queue-with-logic-app/azuredeploy.json index c8e0851c37cb..51d5e8ac1903 100644 --- a/201-alert-to-queue-with-logic-app/azuredeploy.json +++ b/201-alert-to-queue-with-logic-app/azuredeploy.json @@ -81,7 +81,7 @@ } }, "actions": { - "Send_message.": { + "Send_message": { "type": "ApiConnection", "inputs": { "body": { diff --git a/201-alert-to-queue-with-logic-app/azuredeploy.parameters.json b/201-alert-to-queue-with-logic-app/azuredeploy.parameters.json index ec47bed3eccc..043daf4be70a 100644 --- a/201-alert-to-queue-with-logic-app/azuredeploy.parameters.json +++ b/201-alert-to-queue-with-logic-app/azuredeploy.parameters.json @@ -5,14 +5,14 @@ "logicAppName": { "value":"AlertToQueue" }, - "servicebusconnectionString": { + "serviceBusConnectionString": { "value":"GEN-PASSWORD" }, - "servicebusConnectionName": { + "serviceBusConnectionName": { "value":"AlertQueue" }, - "servicebusQueueName": { + "serviceBusQueueName": { "value":"myQueue" } } -} \ No newline at end of file +} diff --git a/201-front-door-rate-limiting/azuredeploy.json b/201-front-door-rate-limiting/azuredeploy.json index 4e8940921dbb..e6e0a7447aba 100644 --- a/201-front-door-rate-limiting/azuredeploy.json +++ b/201-front-door-rate-limiting/azuredeploy.json @@ -90,7 +90,8 @@ } ], "acceptedProtocols": [ - "Http" + "Http", + "Https" ], "patternsToMatch": [ "/*" @@ -129,6 +130,7 @@ "backends": [ { "address": "[parameters('backendAddress')]", + "backendHostHeader": "[parameters('backendAddress')]", "httpPort": 80, "httpsPort": 443, "weight": 50, diff --git a/201-key-vault-secret-create/README.md b/201-key-vault-secret-create/README.md index 070a2bff7855..98b9dbc26f03 100644 --- a/201-key-vault-secret-create/README.md +++ b/201-key-vault-secret-create/README.md @@ -1,3 +1,5 @@ +# Create an Azure Key Vault and a list of secrets + @@ -5,9 +7,24 @@ -This template helps you to create a Key Vault. It allows to create and set multiple access policies and Secrets while creating the Vault. If you are new to [Key Vault check this out](https://azure.microsoft.com/en-us/services/key-vault/). A full walk through of this template is available [here](http://www.rahulpnath.com/blog/managing-azure-key-vault-using-azure-resource-manager-arm-templates/). +This template creates a key vault with a multiple access policies, and a list of secrets. Instead of just using an array for the secret creation, this template wraps an array in a [secureObject](https://docs.microsoft.com/azure/azure-resource-manager/resource-group-authoring-templates#parameters). Using a secureObject instead of an array type means that the values you pass, cannot be read back in the portal after the deployment. + +Resource iteration is used in this template. For more information, see + +- [Create multiple instances](https://docs.microsoft.com/azure/azure-resource-manager/resource-group-create-multiple) +- [Tutorial: create multiple instances](https://docs.microsoft.com/azure/azure-resource-manager/resource-manager-tutorial-create-multiple-instances) + +If you are new to Azure Key Vault, see: + +- [Azure Key Vault service](https://azure.microsoft.com/services/key-vault/) +- [Azure Key Vault documentation](https://docs.microsoft.com/azure/key-vault/) +- [Azure Key Vault template reference](https://docs.microsoft.com/azure/templates/microsoft.keyvault/allversions) +- [Quickstart templates](https://azure.microsoft.com/resources/templates/?resourceType=Microsoft.Keyvault) + +If you are new to the template development, see: -Instead of just using an array for the secret creation, this template wraps an array in a [secureObject](https://docs.microsoft.com/en-us/azure/azure-resource-manager/resource-group-authoring-templates#parameters). -Using a secureObject instead of an array type means that the values you pass, cannot be read back in the portal after the deployment. +- [Azure Resource Manager documentation](https://docs.microsoft.com/en-us/azure/azure-resource-manager/) +- [Use Azure Key Vault to pass secure parameter value during deployment](https://docs.microsoft.com/azure/azure-resource-manager/resource-manager-keyvault-parameter) +- [Tutorial: Integrate Azure Key Vault in Resource Manager Template deployment](https://docs.microsoft.com/azure/azure-resource-manager/resource-manager-tutorial-use-key-vault) -Tags: Azure Key Vault, Key Vault, Secrets +Tags: Azure Key Vault, Key Vault, Secrets, Resource Manager, Resource Manager templates, ARM templates diff --git a/201-key-vault-secret-create/azuredeploy.json b/201-key-vault-secret-create/azuredeploy.json index 99c3090b14f6..796361a8c947 100644 --- a/201-key-vault-secret-create/azuredeploy.json +++ b/201-key-vault-secret-create/azuredeploy.json @@ -5,66 +5,96 @@ "keyVaultName": { "type": "string", "metadata": { - "description": "Name of the Key Vault" + "description": "Specifies the name of the key vault." } }, - "tenantId": { + "location": { "type": "string", + "defaultValue": "[resourceGroup().location]", "metadata": { - "description": "Tenant Id for the subscription and use assigned access to the vault. Available from the Get-AzureRMSubscription PowerShell cmdlet" - } - }, - "accessPolicies": { - "type": "array", - "defaultValue": "{}", - "metadata": { - "description": "Access policies object {\"tenantId\":\"\",\"objectId\":\"\",\"permissions\":{\"keys\":[\"\"],\"secrets\":[\"\"]}}" + "description": "Specifies the Azure location where the key vault should be created." } }, - "vaultSku": { - "type": "string", - "defaultValue": "Standard", + "enabledForDeployment": { + "type": "bool", + "defaultValue": false, "allowedValues": [ - "Standard", - "Premium" + true, + false ], "metadata": { - "description": "SKU for the vault" + "description": "Specifies whether Azure Virtual Machines are permitted to retrieve certificates stored as secrets from the key vault." } }, - "enabledForDeployment": { + "enabledForDiskEncryption": { "type": "bool", "defaultValue": false, + "allowedValues": [ + true, + false + ], "metadata": { - "description": "Specifies if the vault is enabled for VM or Service Fabric deployment" + "description": "Specifies whether Azure Disk Encryption is permitted to retrieve secrets from the vault and unwrap keys." } }, "enabledForTemplateDeployment": { "type": "bool", "defaultValue": false, + "allowedValues": [ + true, + false + ], "metadata": { - "description": "Specifies if the vault is enabled for ARM template deployment" + "description": "Specifies whether Azure Resource Manager is permitted to retrieve secrets from the key vault." } }, - "enableVaultForVolumeEncryption": { - "type": "bool", - "defaultValue": false, + "tenantId": { + "type": "string", + "defaultValue": "[subscription().tenantId]", "metadata": { - "description": "Specifies if the vault is enabled for volume encryption" + "description": "Specifies the Azure Active Directory tenant ID that should be used for authenticating requests to the key vault. Get it by using Get-AzSubscription cmdlet." } }, - "secretsObject": { - "type": "secureObject", - "defaultValue": "{}", + "objectId": { + "type": "string", "metadata": { - "description": "all secrets {\"secretName\":\"\",\"secretValue\":\"\"} wrapped in a secure object" + "description": "Specifies the object ID of a user, service principal or security group in the Azure Active Directory tenant for the vault. The object ID must be unique for the list of access policies. Get it by using Get-AzADUser or Get-AzADServicePrincipal cmdlets." } }, - "location": { + "keysPermissions": { + "type": "array", + "defaultValue": [ + "list" + ], + "metadata": { + "description": "Specifies the permissions to keys in the vault. Valid values are: all, encrypt, decrypt, wrapKey, unwrapKey, sign, verify, get, list, create, update, import, delete, backup, restore, recover, and purge." + } + }, + "secretsPermissions": { + "type": "array", + "defaultValue": [ + "list" + ], + "metadata": { + "description": "Specifies the permissions to secrets in the vault. Valid values are: all, get, list, set, delete, backup, restore, recover, and purge." + } + }, + "skuName": { "type": "string", - "defaultValue": "[resourceGroup().location]", + "defaultValue": "Standard", + "allowedValues": [ + "Standard", + "Premium" + ], + "metadata": { + "description": "Specifies whether the key vault is a standard vault or a premium vault." + } + }, + "secretsObject": { + "type": "secureObject", + "defaultValue": "{}", "metadata": { - "description": "Location for all resources." + "description": "Specifies all secrets {\"secretName\":\"\",\"secretValue\":\"\"} wrapped in a secure object." } } }, @@ -72,37 +102,52 @@ { "type": "Microsoft.KeyVault/vaults", "name": "[parameters('keyVaultName')]", - "apiVersion": "2015-06-01", "location": "[parameters('location')]", + "apiVersion": "2018-02-14", "tags": { "displayName": "KeyVault" }, "properties": { "enabledForDeployment": "[parameters('enabledForDeployment')]", "enabledForTemplateDeployment": "[parameters('enabledForTemplateDeployment')]", - "enabledForVolumeEncryption": "[parameters('enableVaultForVolumeEncryption')]", + "enabledForDiskEncryption": "[parameters('enabledForDiskEncryption')]", "tenantId": "[parameters('tenantId')]", - "accessPolicies": "[parameters('accessPolicies')]", + "accessPolicies": [ + { + "objectId": "[parameters('objectId')]", + "tenantId": "[parameters('tenantId')]", + "permissions": { + "keys": "[parameters('keysPermissions')]", + "secrets": "[parameters('secretsPermissions')]" + } + } + ], "sku": { - "name": "[parameters('vaultSku')]", + "name": "[parameters('skuName')]", "family": "A" + }, + "networkAcls": { + "value": { + "defaultAction": "Allow", + "bypass": "AzureServices" + } } } }, { "type": "Microsoft.KeyVault/vaults/secrets", "name": "[concat(parameters('keyVaultName'), '/', parameters('secretsObject').secrets[copyIndex()].secretName)]", - "apiVersion": "2015-06-01", - "properties": { - "value": "[parameters('secretsObject').secrets[copyIndex()].secretValue]" - }, + "apiVersion": "2018-02-14", "dependsOn": [ "[concat('Microsoft.KeyVault/vaults/', parameters('keyVaultName'))]" ], "copy": { "name": "secretsCopy", "count": "[length(parameters('secretsObject').secrets)]" + }, + "properties": { + "value": "[parameters('secretsObject').secrets[copyIndex()].secretValue]" } } ] -} +} \ No newline at end of file diff --git a/201-key-vault-secret-create/azuredeploy.parameters.json b/201-key-vault-secret-create/azuredeploy.parameters.json index 226382a0783b..45477c8a4450 100644 --- a/201-key-vault-secret-create/azuredeploy.parameters.json +++ b/201-key-vault-secret-create/azuredeploy.parameters.json @@ -1,46 +1,26 @@ - { - "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#", - "contentVersion": "1.0.0.0", - "parameters": { - "keyVaultName": { - "value": "testVault" - }, - "tenantId": { - "value": "example-guid" - }, - "accessPolicies": { - "value": [ - { - "tenantId": "example-guid", - "objectId": "example-guid", - "permissions": { - "keys": ["all"], - "secrets": ["all"] - } - }, - { - "tenantId": "example-guid", - "objectId": "example-guid", - "permissions": { - "keys": ["all"], - "secrets": ["all"] - } - } - ] - }, - "secretsObject": { - "value": { - "secrets": [ - { - "secretName": "exampleSecret1", - "secretValue": "secretVaule1" - }, - { - "secretName": "exampleSecret2", - "secretValue": "secretValue2" - } - ] - } - } - } - } \ No newline at end of file +{ + "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "GEN-UNIQUE" + }, + "objectId": { + "value": "GEN-AZUREAD-OBJECTID" + }, + "secretsObject": { + "value": { + "secrets": [ + { + "secretName": "exampleSecret1", + "secretValue": "secretVaule1" + }, + { + "secretName": "exampleSecret2", + "secretValue": "secretValue2" + } + ] + } + } + } +} diff --git a/201-key-vault-secret-create/metadata.json b/201-key-vault-secret-create/metadata.json index 8e0c6eceac00..c7996874b2dd 100644 --- a/201-key-vault-secret-create/metadata.json +++ b/201-key-vault-secret-create/metadata.json @@ -1,11 +1,11 @@ { "$schema": "https://aka.ms/azure-quickstart-templates-metadata-schema#", "type": "QuickStart", - "itemDisplayName": "Create Key Vault", - "description": "This template creates a Key Vault and creates secrets within the vault as passed along with the parameters", - "summary": "Creates a Key Vault with dynammic list of Secrets", + "itemDisplayName": "Create a Key Vault and a list of secrets", + "description": "This template creates a Key Vault and a list of secrets within the key vault as passed along with the parameters", + "summary": "Creates a Key Vault with a list of secrets", "githubUsername": "rahulpnath", - "dateUpdated": "2017-10-23" + "dateUpdated": "2019-02-14" } diff --git a/201-key-vault-with-logging-create/README.md b/201-key-vault-with-logging-create/README.md index 85a059fa739c..e448ec1d1754 100644 --- a/201-key-vault-with-logging-create/README.md +++ b/201-key-vault-with-logging-create/README.md @@ -1,3 +1,4 @@ +# Create an Azure Key Vault with logging enabled @@ -5,6 +6,19 @@ -This template helps you to create a Key Vault with diagnostics enabled. It allows to create and set multiple access policies while creating the Vault. If you are new to [Key Vault check this out](https://azure.microsoft.com/en-us/services/key-vault/). A full walk through of this template is available [here](https://www.codeisahighway.com/creating-azure-key-vault-with-logging-using-azure-resource-manager-arm-templates/). +This template creates an Azure Key Vault with diagnostics/logging enabled. For more information, see [Azure Key Vault Logging]https://docs.microsoft.com/azure/key-vault/key-vault-logging). -Tags: Azure Key Vault, Key Vault, Diagnostics, Resource Locks \ No newline at end of file +If you are new to Azure Key Vault, see: + +- [Azure Key Vault service](https://azure.microsoft.com/services/key-vault/) +- [Azure Key Vault documentation](https://docs.microsoft.com/azure/key-vault/) +- [Azure Key Vault template reference](https://docs.microsoft.com/azure/templates/microsoft.keyvault/allversions) +- [Quickstart templates](https://azure.microsoft.com/resources/templates/?resourceType=Microsoft.Keyvault) + +If you are new to the template development, see: + +- [Azure Resource Manager documentation](https://docs.microsoft.com/en-us/azure/azure-resource-manager/) +- [Use Azure Key Vault to pass secure parameter value during deployment](https://docs.microsoft.com/azure/azure-resource-manager/resource-manager-keyvault-parameter) +- [Tutorial: Integrate Azure Key Vault in Resource Manager Template deployment](https://docs.microsoft.com/azure/azure-resource-manager/resource-manager-tutorial-use-key-vault) + +Tags: Azure Key Vault, Key Vault, Secrets, Resource Manager, Resource Manager templates, ARM templates, diagnostics, resource locks, logging diff --git a/201-key-vault-with-logging-create/metadata.json b/201-key-vault-with-logging-create/metadata.json index bb5adc5c542e..c785dd141fcc 100644 --- a/201-key-vault-with-logging-create/metadata.json +++ b/201-key-vault-with-logging-create/metadata.json @@ -1,11 +1,11 @@ { "$schema": "https://aka.ms/azure-quickstart-templates-metadata-schema#", "type": "QuickStart", - "itemDisplayName": "Create Key Vault with logging", - "description": "This template creates a Key Vault and a storage account that is used for logging. It optionally creates resource locks to protect your Key Vault and storage resources.", + "itemDisplayName": "Create Key Vault with logging enabled", + "description": "This template creates an Azure Key Vault and an Azure Storage account that is used for logging. It optionally creates resource locks to protect your Key Vault and storage resources.", "summary": "Creates and optionally secures a Key Vault with logging linked to a storage account.", "githubUsername": "slapointe", - "dateUpdated": "2017-02-15" + "dateUpdated": "2019-01-24" } diff --git a/201-storage-advanced-threat-protection-create/README.md b/201-storage-advanced-threat-protection-create/README.md new file mode 100644 index 000000000000..d67b55873bb1 --- /dev/null +++ b/201-storage-advanced-threat-protection-create/README.md @@ -0,0 +1,14 @@ +# Deploy an Azure Storage Account with Advanced Threat Protection enabled + + + + + + + + +This template allows you to deploy an Azure Storage Account with Advanced Threat Protection enabled. + +Storage Advanced Threat Protection is a unified package for advanced Storage security capabilities. + +For more information on Storage Advanced Threat Protection, see the [official documentation]( https://docs.microsoft.com/en-us/azure/storage/common/storage-advanced-threat-protection). \ No newline at end of file diff --git a/201-storage-advanced-threat-protection-create/azuredeploy.json b/201-storage-advanced-threat-protection-create/azuredeploy.json new file mode 100644 index 000000000000..a73490a8a591 --- /dev/null +++ b/201-storage-advanced-threat-protection-create/azuredeploy.json @@ -0,0 +1,84 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "storageAccountName": { + "type": "string", + "defaultValue": "[concat('atpstorage', uniqueString(resourceGroup().id))]", + "metadata": { + "description": "The name must be unique across all existing storage account names in Azure. It must be 3 to 24 characters long, and can contain only lowercase letters and numbers." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Storage account location, default is same as resource group location." + } + }, + "storageAccountKind": { + "type": "string", + "defaultValue": "StorageV2", + "allowedValues": [ + "StorageV2", + "Storage" + ], + "metadata": { + "description": "Storage account type, for more info see 'https://docs.microsoft.com/en-us/azure/storage/common/storage-account-overview'." + } + }, + "storageAccountReplication": { + "type": "string", + "defaultValue": "Standard_LRS", + "allowedValues": [ + "Standard_LRS", + "Standard_GRS", + "Standard_ZRS", + "Premium_LRS" + ], + "metadata": { + "description": "Storage account replication, for more info see 'https://docs.microsoft.com/en-us/azure/storage/common/storage-redundancy'." + } + }, + "advancedThreatProtectionEnabled": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Enable or disable Advanced Threat Protection." + } + } + }, + "resources": [ + { + "type": "Microsoft.Storage/storageAccounts", + "name": "[parameters('storageAccountName')]", + "location": "[parameters('location')]", + "apiVersion": "2018-07-01", + "sku": { + "name": "[parameters('storageAccountReplication')]" + }, + "kind": "[parameters('storageAccountKind')]", + "properties": {}, + "resources": [ + { + "condition": "[parameters('advancedThreatProtectionEnabled')]", + "type": "providers/advancedThreatProtectionSettings", + "name": "Microsoft.Security/current", + "apiVersion": "2017-08-01-preview", + "dependsOn": [ + "[resourceId('Microsoft.Storage/storageAccounts/', parameters('storageAccountName'))]" + ], + "properties": { + "isEnabled": true + } + } + ] + } + ], + "outputs": { + "storageAccountName": { + "type": "string", + "value": "[parameters('storageAccountName')]" + } + } +} \ No newline at end of file diff --git a/201-storage-advanced-threat-protection-create/azuredeploy.parameters.json b/201-storage-advanced-threat-protection-create/azuredeploy.parameters.json new file mode 100644 index 000000000000..d95b533b058e --- /dev/null +++ b/201-storage-advanced-threat-protection-create/azuredeploy.parameters.json @@ -0,0 +1,9 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "storageAccountName": { + "value": "GEN-UNIQUE" + } + } +} \ No newline at end of file diff --git a/201-storage-advanced-threat-protection-create/metadata.json b/201-storage-advanced-threat-protection-create/metadata.json new file mode 100644 index 000000000000..da1fe081cc8f --- /dev/null +++ b/201-storage-advanced-threat-protection-create/metadata.json @@ -0,0 +1,9 @@ +{ + "$schema": "https://aka.ms/azure-quickstart-templates-metadata-schema#", + "type": "QuickStart", + "itemDisplayName": "Storage account with Advanced Threat Protection.", + "description": "This template allows you to deploy an Azure Storage account with Advanced Threat Protection enabled.", + "summary": "Deploy an Azure Storage account with Advanced Threat Protection enabled.", + "githubUsername": "neryaco", + "dateUpdated": "2019-02-17" +} \ No newline at end of file diff --git a/application-gateway-logviewer-goaccess/README.md b/application-gateway-logviewer-goaccess/README.md index 754cfcfa0620..af4f692ac1a9 100644 --- a/application-gateway-logviewer-goaccess/README.md +++ b/application-gateway-logviewer-goaccess/README.md @@ -19,7 +19,12 @@ By default, GoAccess installed by this template will parse and display traffic s
  1. Only the ApplicationGatewayAccessLog will be used by GoAccess
  2. You want to make sure you are sending and storing your ApplicationGatewayAccessLog to a storage account (select the “Archive to a storage account” check box if using the Portal to enable Application Gateway logging).
  3. -
  4. In your storage account settings, ensure you have Shared Access Signature key configured. The expiry date time needs to be set to a date much further out in the future (eg: 1 year out from now). Also, only the Read and List permissions are needed for GoAccess. Make sure to generate the connection string as well. The Blob service SAS URL connection string is what you need to input to the ARM template.
  5. +
  6. In your storage account container, ensure you have Shared Access Signature key configured. The expiry date time needs to be set to a date much further out in the future (eg: 1 year out from now). Also, only the Read and List permissions are needed for GoAccess. Make sure to generate the connection string as well. The Blob Container Service SAS URL connection string is what you need to input to the ARM template. +

    You can generate Service-level SAS URL for the Blob Container "insights-logs-applicationgatewayaccesslog" using Azure Storage Explorer for your operating system. Storage Explorer is available for Windows, MacOS and Linux.

    + For example, the blob container SAS URL should look like this -

    https://[your-blob-url]/insights-logs-applicationgatewayaccesslog?st=2019-02-08T12%3A55%3A14Z&se=2020-02-09T12%3A55%3A00Z&sp=rl&sv=2018-03-28&sr=c&sig=jcfAjefo3TitH7kl9YC15COaSdfgMmPFnO8QTI6oY9c%3D


    + +

    Alternatively, you can generate the Service-level SAS using REST API. Read more about it here. +
@@ -33,7 +38,7 @@ The template will require a set of parameters input from you as the user:
  • adminUsername: Username you want to use for the VM the template creates
  • adminPassword: Password you want to use to log in to the VM
  • dnsNameForPublicIP: The DNS name (prefix) you want to use for the VM to map against its public IP
  • -
  • appGwAccessLogsBlobSasUri: The SAS URL connection string (see 2(c) in the Pre-requisites list above) for the storage account blog where your Application Gateway Access Logs are stored
  • +
  • appGwAccessLogsBlobSasUri: The SAS URL connection string (see 2(iii) in the Pre-requisites list above) for the storage account blog where your Application Gateway Access Logs are stored
  • FilterRegexForAppGwAccessLogs: A regex to use to filter the Application Gateway Access Logs to a specific subset. For example, if you have multiple application gateways publishing logs to the same storage account blob, and you only want GoAccess to surface traffic stats for say one of the Application Gateways, you can provide a regex for this field to filter to just that instance.
  • Region: The Azure region where you would like the VM to be created
  • @@ -56,6 +61,8 @@ Please note following aspects related to this template: By default, the GoAccess dashboard and associated data are unsecured. Since the web server is Apache HTTP Webserver, you can secure access by following the Apache Auth documentation. +Also, since it is a Virtual Machine, you can use Network Security Groups to allow/deny IP addresses to restrict access, but make sure that outbound internet connectivity is allowed to reach the storage account. +

    Getting Help

    For any issues with running this template, please file an issue in GitHub under Azure/azure-quickstart-templates repository: https://github.com/Azure/azure-quickstart-templates/issues diff --git a/oms-cloudfoundry-solution/azuredeploy.json b/oms-cloudfoundry-solution/azuredeploy.json index 481b5c1c5964..8447cfb0054c 100644 --- a/oms-cloudfoundry-solution/azuredeploy.json +++ b/oms-cloudfoundry-solution/azuredeploy.json @@ -72,6 +72,12 @@ "description": "Select your provider of system metrics" } }, + "actionGroupName": { + "type": "string", + "metadata": { + "description": "The name of the action group for alert actions" + } + }, "_artifactsLocation": { "type": "string", "defaultValue": "https://raw.githubusercontent.com/Azure/azure-quickstart-templates/master/oms-cloudfoundry-solution", @@ -187,6 +193,9 @@ "parameters": { "omsWorkspaceName": { "value": "[parameters('omsWorkspaceName')]" + }, + "actionGroupName": { + "value": "[parameters('actionGroupName')]" } } } diff --git a/oms-cloudfoundry-solution/azuredeploy.parameters.json b/oms-cloudfoundry-solution/azuredeploy.parameters.json index a711d9846142..96443674ef6a 100644 --- a/oms-cloudfoundry-solution/azuredeploy.parameters.json +++ b/oms-cloudfoundry-solution/azuredeploy.parameters.json @@ -13,6 +13,9 @@ }, "systemMetricsProvider": { "value": "None" + }, + "actionGroupName": { + "value": "None" } } } \ No newline at end of file diff --git a/oms-cloudfoundry-solution/nested/omsAlerts.json b/oms-cloudfoundry-solution/nested/omsAlerts.json index 2de1f09d8e0c..08d72050f634 100644 --- a/oms-cloudfoundry-solution/nested/omsAlerts.json +++ b/oms-cloudfoundry-solution/nested/omsAlerts.json @@ -7,6 +7,12 @@ "metadata": { "description": "Input the name of your OMS Workspace" } + }, + "actionGroupName": { + "type": "string", + "metadata": { + "description": "The name of the action group for alert actions" + } } }, "variables": { @@ -1767,7 +1773,7 @@ "properties": { "etag": "*", "Type": "Alert", - "name": "[variables('alerts')[copyIndex()].alert.displayName]", + "Name": "[variables('alerts')[copyIndex()].alert.displayName]", "Description": "[variables('alerts')[copyIndex()].alert.description]", "Severity": "[toLower(variables('alerts')[copyIndex()].alert.severity)]", "Threshold": { @@ -1776,8 +1782,12 @@ }, "Throttling": { "DurationInMinutes": "[variables('alerts')[copyIndex()].alert.throttleMinutes]" + }, + "AzNsNotification": { + "GroupIds": ["[concat(resourceGroup().id, '/providers/microsoft.insights/actiongroups/', parameters('actionGroupName'))]"], + "CustomEmailSubject": "[concat('[', toUpper(variables('alerts')[copyIndex()].alert.severity), '] ', variables('alerts')[copyIndex()].alert.displayName)]" } } - } + } ] } \ No newline at end of file diff --git a/safekit-cluster-farm/README.md b/safekit-cluster-farm/README.md new file mode 100644 index 000000000000..40f61827d253 --- /dev/null +++ b/safekit-cluster-farm/README.md @@ -0,0 +1,59 @@ +# Evidian SafeKit - Load Balancing Cluster with Failover in Azure - Farm Module + + + + + +* [Description](#description) +* [Deployed resources](#resources) +* [How to use](#use) +* [More information](#more) + +## Description + +![How the Evidian SafeKit farm cluster implements load balancing and failover in Azure?](images/farmarch.png) + +On the previous figure, + +* the critical application is running in all servers of the farm, +* users are connected to a virtual IP address which is configured in the Azure load balancer. +SafeKit brings a generic health probe for the load balancer. When the farm module is stopped in a server, the health probe returns NOK to the load balancer which stops the load balancing of requests to the server. The same behavior happens when there is a **hardware failure**. +* in each server, SafeKit monitors the critical application with process checkers and custom checkers; +* SafeKit restarts automatically the critical application in a server when there is a **software failure** thanks to the restart scripts; +* a connector for the SafeKit web console is installed in each server. Thus, the load balancing cluster can be managed in a very simple way to avoid **human errors**. + +## Deployed resources + +In term of VMs, this template deploys: + +* from 2 to 4 VMs (Windows or Linux) spanning 2 or 3 availability zone(s) +* each VM has a public IP address (Standard SKU) +* the SafeKit free trial is installed in all VMs +* a SafeKit farm module is configured in all VMs + +In term of load balancer, this template deploys: + +* a public load balancer (standard SKU) +* a public IP (Standard SKU) is associated with the public load balancer and plays the role of the virtual IP +* alls VMs are in the backend pool of the load balancer +* a health probe checks the farm module state on all VMs +* a load balancing rule for external port 9453 / internal port 9453 is set to test the load balanced virtual IP + +## How to use + +Click the "Deploy to Azure" button at the beginning of this document to deploy the load balancing cluster. Please create a new resource group. + +After deployment, go to the resource group's 'Microsoft.Template' deployment output panel (Home > Resource Groups > YourResourceGroup - Deployments) and: + +* visit the credential url to install the client and CA certificates in your web browser +* after certificates installation, start the web console of the cluster +* test the load balanced virtual IP address with the test URL in the output + + + +## More information on **Evidian SafeKit** in Azure + +* [Azure: The Simplest Load Balancing Cluster with Failover](https://www.evidian.com/products/high-availability-software-for-application-clustering/azure-load-balancing-cluster-failover/) +* [Azure: The Simplest High Availability Cluster with Synchronous Replication and Failover](https://www.evidian.com/products/high-availability-software-for-application-clustering/azure-high-availability-cluster-synchronous-replication-failover/) + +`Tags: load balancing, cluster, failover, high availability, business continuity, disaster recovery, evidian, safekit, farm` \ No newline at end of file diff --git a/safekit-cluster-farm/azuredeploy.json b/safekit-cluster-farm/azuredeploy.json new file mode 100644 index 000000000000..00c8128dde67 --- /dev/null +++ b/safekit-cluster-farm/azuredeploy.json @@ -0,0 +1,245 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "OS": { + "type": "string", + "metadata": { + "description": "Operating System to install" + }, + "defaultValue": "2016-Datacenter", + "allowedValues": [ + "2016-Datacenter", + "2019-Datacenter", + "Linux CentOS" + ] + }, + "clusterNodes": { + "type": "int", + "metadata": { + "description": "number of VM nodes to create" + }, + "defaultValue": 2, + "allowedValues": [ + 2, + 3, + 4 + ] + }, + "vmSize": { + "type": "string", + "metadata": { + "description": "the VM size for all nodes" + }, + "defaultValue": "Standard_A2_v2" + }, + "adminUser": { + "type": "string", + "metadata": { + "description": "User for the Virtual Machines." + } + }, + "adminPassword": { + "type": "securestring", + "metadata": { + "description": "Password for the Virtual Machines." + } + }, + "VIPDnsLabel": { + "type": "string", + "metadata": { + "description": "Public VIP dns label. VIP fqdn will be like ..cloudapp.azure.com and must be globally unique." + }, + "defaultValue": "[concat(uniqueString(resourceGroup().id),'vip')]" + }, + "VMDnsPrefix": { + "type": "string", + "metadata": { + "description": "Public VM IP dns label prefix. VM fqdn will be like vm..cloudapp.azure.com and must be unique." + }, + "defaultValue": "[uniqueString(resourceGroup().id)]" + }, + "azurePwsh": { + "type": "string", + "metadata": { + "description": "install azure powershell module (optional)" + }, + "defaultValue": "no", + "allowedValues": [ + "yes", + "no" + ] + }, + "location": { + "type": "string", + "metadata": { + "description": "resources location (region must support Availability Zones)" + }, + "allowedValues": [ + "centralus", + "eastus2", + "francecentral", + "northeurope", + "southeastasia", + "westeurope", + "westus2" + ] + }, + "safekitFileUri": { + "type": "string", + "metadata": { + "description": "url of SafeKit package (optional, default to the appropriate Evidian website url)." + }, + "defaultValue": "default" + }, + "_artifactsLocation": { + "type": "string", + "metadata": { + "description": "base URL of deployment resources (template,subtemplates,scripts)" + }, + "defaultValue": "https://raw.githubusercontent.com/Azure/azure-quickstart-templates/master/safekit-cluster-farm/" + }, + "_artifactsLocationSasToken": { + "type": "securestring", + "metadata": { + "description": "The sasToken required to access _artifactsLocation. When the template is deployed using the accompanying scripts, a sasToken will be automatically generated. Use the defaultValue if the staging location is not secured." + }, + "defaultValue": "" + } + }, + "variables": { + "clusternodes": "[parameters('clusterNodes')]", + "vmname": "VM1", + "moduleName": "farm", + "cltemplate": "[uri(parameters('_artifactsLocation'),'nestedtemplates/cluster.json')]", + "ostype": "[if(equals(parameters('OS'), 'Linux CentOS'),'linux','windows')]", + "skcfgmoduletemplate": "[concat(uri(parameters('_artifactsLocation'),'nestedtemplates/cfgFarm.json'),parameters('_artifactsLocationSasToken'))]", + "safekitpkgUrl": { + "windows": "https://support.evidian.com/solutions/downloads/safekit/cloud/platforms/windows/current_versions/safekit_cloud.msi", + "linux": "https://support.evidian.com/solutions/downloads/safekit/cloud/platforms/linux/current_versions/safekit_cloud.bin" + }, + "farmUrl": { + "windows": "https://support.evidian.com/solutions/downloads/safekit/cloud/application_modules/windows/farm.safe", + "linux": "https://support.evidian.com/solutions/downloads/safekit/cloud/application_modules/linux/farm.safe" + } + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2018-05-01", + "name": "safekitCluster", + "properties": { + "mode": "Incremental", + "templateLink": { + "uri": "[variables('cltemplate')]", + "contentVersion": "1.0.0.0" + }, + "parameters": { + "OS": { + "value": "[parameters('OS')]" + }, + "clusterNodes": { + "value": "[variables('clusterNodes')]" + }, + "vmSize": { + "value": "[parameters('vmSize')]" + }, + "adminUser": { + "value": "[parameters('adminUser')]" + }, + "adminPassword": { + "value": "[parameters('adminPassword')]" + }, + "moduleUrl": { + "value": "[variables('farmUrl')[variables('ostype')]]" + }, + "moduleName": { + "value": "[variables('moduleName')]" + }, + "VIPDnsLabel": { + "value": "[parameters('VIPDnsLabel')]" + }, + "VMDnsPrefix": { + "value": "[parameters('VMDnsPrefix')]" + }, + "Loadbalancer": { + "value": "External" + }, + "location": { + "value": "[parameters('location')]" + }, + "safekitFileUri": { + "value": "[if(equals(parameters('safekitFileUri'),'default'),variables('safekitpkgUrl')[variables('ostype')],parameters('safekitFileUri'))]" + }, + "_artifactsLocation": { + "value": "[parameters('_artifactsLocation')]" + }, + "_artifactsLocationSasToken": { + "value": "[parameters('_artifactsLocationSasToken')]" + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2018-05-01", + "name": "safekitModuleConfig", + "dependsOn": [ + "safekitCluster" + ], + "properties": { + "mode": "Incremental", + "templateLink": { + "uri": "[variables('skcfgmoduletemplate')]", + "contentVersion": "1.0.0.0" + }, + "parameters": { + "vmname": { + "value": "[variables('vmname')]" + }, + "ostype": { + "value": "[variables('ostype')]" + }, + "moduleName": { + "value": "[variables('moduleName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "_artifactsLocation": { + "value": "[parameters('_artifactsLocation')]" + }, + "_artifactsLocationSasToken": { + "value": "[parameters('_artifactsLocationSasToken')]" + } + } + } + } + ], + "outputs": { + "FIRST GET THE CREDENTIALS": { + "type": "string", + "value": "[reference('safekitCluster').outputs['Credentials Url'].value]" + }, + "LOGIN TO GET THE CREDENTIALS": { + "type": "string", + "value": "[reference('safekitCluster').outputs['Credentials Url Login'].value]" + }, + "START THE CONSOLE": { + "type": "string", + "value": "[concat(reference('safekitCluster').outputs['Console Url'].value,'?firewallDialog=false')]" + }, + "TEST THE VIRTUAL IP": { + "type": "string", + "value": "[reference('safekitCluster').outputs['Mosaic URL'].value]" + }, + "ADMINUSER": { + "type": "string", + "value": "[parameters('adminUser')]" + }, + "VM1": { + "type": "string", + "value": "[reference('safekitCluster').outputs.fqdn.value]" + } + } +} \ No newline at end of file diff --git a/safekit-cluster-farm/azuredeploy.parameters.json b/safekit-cluster-farm/azuredeploy.parameters.json new file mode 100644 index 000000000000..20d8137b22da --- /dev/null +++ b/safekit-cluster-farm/azuredeploy.parameters.json @@ -0,0 +1,21 @@ +{ + "$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "adminUser": { + "value": "GEN-UNIQUE" + }, + "adminPassword": { + "value": "GEN-PASSWORD" + }, + "VIPDnsLabel": { + "value": "GEN-UNIQUE-13" + }, + "VMDnsPrefix": { + "value": "GEN-UNIQUE-10" + }, + "location": { + "value": "westus2" + } + } +} \ No newline at end of file diff --git a/safekit-cluster-farm/images/farmarch.png b/safekit-cluster-farm/images/farmarch.png new file mode 100644 index 000000000000..e7e5c2205b38 Binary files /dev/null and b/safekit-cluster-farm/images/farmarch.png differ diff --git a/safekit-cluster-farm/metadata.json b/safekit-cluster-farm/metadata.json new file mode 100644 index 000000000000..1175d52fb538 --- /dev/null +++ b/safekit-cluster-farm/metadata.json @@ -0,0 +1,11 @@ +{ + "$schema": "https://aka.ms/azure-quickstart-templates-metadata-schema#", + "type": "QuickStart", + "itemDisplayName": "Evidian SafeKit Farm Cluster", + "description": "This template deploys a load balancing cluster with failover on 2 to 4 Windows or Linux VMs in different availability zones", + "summary": "Evidian SafeKit farm cluster on Windows or Linux", + "githubUsername": "d6p", + "dateUpdated": "2019-01-14" +} + + diff --git a/safekit-cluster-farm/nestedtemplates/cfgFarm.json b/safekit-cluster-farm/nestedtemplates/cfgFarm.json new file mode 100644 index 000000000000..3114b3b444a0 --- /dev/null +++ b/safekit-cluster-farm/nestedtemplates/cfgFarm.json @@ -0,0 +1,91 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "vmname": { + "type": "string", + "metadata": { + "description": "vm name" + } + }, + "ostype": { + "type": "string", + "metadata": { + "description": "OS type (windows, linux)" + }, + "defaultValue": "windows" + }, + "moduleName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "url of the application module to install on all nodes" + } + }, + "location": { + "type": "string", + "metadata": { + "description": "resources location" + } + }, + "_artifactsLocation": { + "type": "string", + "metadata": { + "description": "base URL of deployment resources (template,subtemplates,scripts)" + } + }, + "_artifactsLocationSasToken": { + "type": "securestring", + "metadata": { + "description": "The sasToken required to access _artifactsLocation. When the template is deployed using the accompanying scripts, a sasToken will be automatically generated. Use the defaultValue if the staging location is not secured." + } + } + }, + "variables": { + "cfgModuleuri": "[concat(uri(parameters('_artifactsLocation'),'scripts/cfgFarm.ps1'),parameters('_artifactsLocationSasToken'))]", + "properties": { + "windows": { + "publisher": "Microsoft.Compute", + "type": "CustomScriptExtension", + "typeHandlerVersion": "1.9", + "autoUpgradeMinorVersion": true, + "settings": { + "fileUris": [ + "[variables('cfgModuleuri')]" + ] + }, + "protectedSettings":{ + "commandToExecute": "[concat('powershell -ExecutionPolicy Unrestricted -File .\\cfgFarm.ps1 -safekitcmd C:/safekit/safekit -safekitmod C:/safekit/modules -MName \"',parameters('moduleName'),'\"')]" + } + }, + "linux": { + "publisher": "Microsoft.Azure.Extensions", + "type": "CustomScript", + "typeHandlerVersion": "2.0", + "autoUpgradeMinorVersion": true, + "settings": { + "skipDos2Unix": false, + "timestamp": 1, + "fileUris": [ + "[variables('cfgModuleuri')]" + ] + }, + "protectedSettings":{ + "commandToExecute": "[concat('pwsh ./cfgFarm.ps1 -safekitcmd /opt/safekit/safekit -safekitmod /opt/safekit/modules -MName \"',parameters('moduleName'),'\"')]" + } + } + } + }, + "resources": [ + { + "name": "[concat(parameters('vmname'),'/safekit')]", + "type": "Microsoft.Compute/virtualMachines/extensions", + "apiVersion": "2018-06-01", + "location": "[parameters('location')]", + "properties": "[variables('properties')[parameters('ostype')]]" + } + ], + "outputs": { + + } +} \ No newline at end of file diff --git a/safekit-cluster-farm/nestedtemplates/cluster.json b/safekit-cluster-farm/nestedtemplates/cluster.json new file mode 100644 index 000000000000..f800d65849fc --- /dev/null +++ b/safekit-cluster-farm/nestedtemplates/cluster.json @@ -0,0 +1,636 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "OS": { + "type": "string", + "metadata": { + "description": "Operating System to install" + } + }, + "clusterNodes": { + "type": "int", + "metadata": { + "description": "number of VM nodes to create" + }, + "defaultValue": 2, + "allowedValues": [ + 1, + 2, + 3, + 4 + ] + }, + "vmSize": { + "type": "string", + "metadata": { + "description": "the VM size for all nodes" + }, + "defaultValue": "Standard_A2_v2" + }, + "adminUser": { + "type": "string", + "metadata": { + "description": "User for the Virtual Machines." + } + }, + "adminPassword": { + "type": "securestring", + "metadata": { + "description": "Password for the Virtual Machines." + } + }, + "moduleUrl": { + "type": "string", + "metadata": { + "description": "url of the application module to install on all nodes (optional)" + }, + "defaultValue": "" + }, + "moduleName": { + "type": "string", + "metadata": { + "description": "name of the application module to install on all nodes (optional)" + }, + "defaultValue": "mirror" + }, + "VIPDnsLabel": { + "type": "string", + "metadata": { + "description": "Public VIP dns label (optional. If set, an additionnal Standard SKU, unassociated public IP will be created)" + }, + "defaultValue": "" + }, + "VMDnsPrefix": { + "type": "string", + "metadata": { + "description": "Public VM IP dns label prefix" + } + }, + "Loadbalancer": { + "type": "string", + "metadata": { + "description": "loadbalancer (optional. If set, a loadbalancer will be created, with the VIP as frontend and the VMs in the backend pool." + }, + "defaultValue": "none", + "allowedValues": [ + "External", + "none" + ] + }, + "azurePwsh": { + "type": "string", + "metadata": { + "description": "install azure powershell module (optional)" + }, + "defaultValue": "no", + "allowedValues": [ + "yes", + "no" + ] + }, + "location": { + "type": "string", + "metadata": { + "description": "resources location" + } + }, + "safekitFileUri": { + "type": "string", + "metadata": { + "description": "url of safekit package" + }, + "defaultValue": "" + }, + "_artifactsLocation": { + "type": "string", + "metadata": { + "description": "base URL of deployment resources (template,subtemplates,scripts)" + } + }, + "_artifactsLocationSasToken": { + "type": "securestring", + "metadata": { + "description": "The sasToken required to access _artifactsLocation. When the template is deployed using the accompanying scripts, a sasToken will be automatically generated. Use the defaultValue if the staging location is not secured." + } + } + }, + "variables": { + "allvms": [ + "VM1", + "VM2", + "VM3", + "VM4" + ], + "vms": "[take(variables('allvms'),parameters('clusterNodes'))]", + "cltemplate": "[uri(parameters('_artifactsLocation'),'nested/cluster.json')]", + "frontEndIPConfigName": "[concat(parameters('VIPDnsLabel'),'LBFE')]", + "lbPoolName": "[concat(parameters('VIPDnsLabel'),'BEP')]", + "lbProbeName": "[concat(parameters('VIPDnsLabel'),'LBP')]", + "lbName": "[concat(parameters('VIPDnsLabel'),'LB')]", + "NetworkContributor": "[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Authorization/roleDefinitions/','4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "ostype": "[if(equals(parameters('OS'), 'Linux CentOS'),'linux','windows')]", + "adminUsername": "[if(empty(parameters('adminUser')),concat('admin',resourceGroup().name),parameters('adminUser'))]", + "ips": [ + "10.0.0.10", + "10.0.0.11", + "10.0.0.12", + "10.0.0.13" + ], + "sktemplate": "[concat(uri(parameters('_artifactsLocation'), 'nestedtemplates/safekitdepl.json'),parameters('_artifactsLocationSasToken'))]", + "skclustertemplate": "[concat(uri(parameters('_artifactsLocation'), 'nestedtemplates/installCluster.json'),parameters('_artifactsLocationSasToken'))]", + "createviptemplate": "[concat(uri(parameters('_artifactsLocation'),'nestedtemplates/createvip.json'),parameters('_artifactsLocationSasToken'))]", + "skmoduletemplate": "[concat(uri(parameters('_artifactsLocation'),'nestedtemplates/installModule.json'),parameters('_artifactsLocationSasToken'))]", + "NSGName": "SafeKit-NSG", + "storageAccountName": "[concat(uniquestring(resourceGroup().id,parameters('location')), 'stor')]", + "lbPoolID": "[if(equals(parameters('LoadBalancer'),'none'),'',resourceId('Microsoft.Network/loadBalancers/backendAddressPools',variables('lbName'),variables('lbPoolName')))]", + "lbaddrpool1": [ + { + "id": "[variables('lbPoolID')]" + } + ], + "lbaddrpool0": [ + + ], + "lbaddrpool": "[if(greater(length(variables('lbPoolID')),0),variables('lbaddrpool1'),variables('lbaddrpool0'))]", + "WindowsStorageProfile": { + "imageReference": { + "publisher": "MicrosoftWindowsServer", + "offer": "WindowsServer", + "sku": "[parameters('OS')]", + "version": "latest" + }, + "osDisk": { + "createOption": "FromImage" + } + }, + "CentosStorageProfile": { + "imageReference": { + "publisher": "OpenLogic", + "offer": "CentOS", + "sku": "7.5", + "version": "latest" + }, + "osDisk": { + "createOption": "fromImage" + }, + "dataDisks": [ + + ] + }, + "linuxConfiguration": { + "disablePasswordAuthentication": "false" + }, + "addressPrefix": "10.0.0.0/16", + "virtualNetworkName": "[concat(resourceGroup().name,'VNET')]", + "subnetName": "Subnet", + "subnetPrefix": "10.0.0.0/24" + }, + "resources": [ + { + "type": "Microsoft.Network/virtualNetworks", + "apiVersion": "2018-08-01", + "name": "[variables('virtualNetworkName')]", + "location": "[parameters('location')]", + "properties": { + "addressSpace": { + "addressPrefixes": [ + "[variables('addressPrefix')]" + ] + }, + "subnets": [ + { + "name": "[variables('subnetName')]", + "properties": { + "addressPrefix": "[variables('subnetPrefix')]" + } + } + ] + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2018-05-01", + "name": "optionalVip", + "properties": { + "mode": "Incremental", + "templateLink": { + "uri": "[variables('createviptemplate')]", + "contentVersion": "1.0.0.0" + }, + "parameters": { + "location": { + "value": "[parameters('location')]" + }, + "VIPDnsLabel": { + "value": "[parameters('VIPDnsLabel')]" + } + } + } + }, + { + "condition": "[and(not(empty(parameters('VIPDnsLabel'))),not(equals(parameters('Loadbalancer'),'none')))]", + "type": "Microsoft.Network/loadBalancers", + "apiVersion": "2018-08-01", + "name": "[variables('lbName')]", + "location": "[parameters('location')]", + "dependsOn": [ + "optionalVip" + ], + "sku": { + "name": "Standard" + }, + "properties": { + "frontendIPConfigurations": [ + { + "name": "[variables('frontEndIPConfigName')]", + "properties": { + "publicIPAddress": { + "id": "[resourceId('Microsoft.Network/publicIPAddresses',concat('pip',parameters('VIPDnsLabel')))]" + } + } + } + ], + "backendAddressPools": [ + { + "name": "[variables('lbPoolName')]" + } + ], + "loadBalancingRules": [ + { + "name": "LBRule", + "properties": { + "frontendIPConfiguration": { + "id": "[resourceId('Microsoft.Network/loadBalancers/frontendIPConfigurations',variables('lbName'),variables('frontEndIPConfigName'))]" + }, + "backendAddressPool": { + "id": "[resourceId('Microsoft.Network/loadBalancers/backendAddressPools',variables('lbName'),variables('lbPoolName'))]" + }, + "protocol": "tcp", + "frontendPort": 9453, + "backendPort": 9453, + "enableFloatingIP": false, + "idleTimeoutInMinutes": 5, + "probe": { + "id": "[resourceId('Microsoft.Network/loadBalancers/probes',variables('lbName'),variables('lbProbeName'))]" + } + } + } + ], + "probes": [ + { + "name": "[variables('lbProbeName')]", + "properties": { + "protocol": "http", + "port": 9010, + "requestPath": "[concat('/var/modules/',parameters('moduleName'),'/ready.txt')]", + "intervalInSeconds": 5, + "numberOfProbes": 2 + } + } + ] + } + }, + { + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2018-02-01", + "name": "[variables('storageAccountName')]", + "location": "[parameters('location')]", + "sku": { + "name": "Standard_LRS" + }, + "kind": "Storage", + "properties": { + + } + }, + { + "type": "Microsoft.Network/networkSecurityGroups", + "apiVersion": "2018-07-01", + "name": "[variables('NSGName')]", + "location": "[parameters('location')]", + "properties": { + "securityRules": [ + { + "name": "remoteaccess", + "properties": { + "description": "Allow Remote Access", + "protocol": "Tcp", + "sourcePortRange": "*", + "destinationPortRange": "[if(equals(variables('ostype'),'linux'),'22','3389')]", + "sourceAddressPrefix": "Internet", + "destinationAddressPrefix": "*", + "access": "Allow", + "priority": 100, + "direction": "Inbound" + } + }, + { + "name": "secconsole", + "properties": { + "description": "Allow Web Console Security Access", + "protocol": "Tcp", + "sourcePortRange": "*", + "destinationPortRange": "9001", + "sourceAddressPrefix": "Internet", + "destinationAddressPrefix": "*", + "access": "Allow", + "priority": 110, + "direction": "Inbound" + } + }, + { + "name": "webconsole", + "properties": { + "description": "Allow Web Console Access", + "protocol": "Tcp", + "sourcePortRange": "*", + "destinationPortRange": "9453", + "sourceAddressPrefix": "Internet", + "destinationAddressPrefix": "*", + "access": "Allow", + "priority": 120, + "direction": "Inbound" + } + } + ] + } + }, + { + "type": "Microsoft.Network/publicIPAddresses", + "apiVersion": "2018-07-01", + "name": "[concat('pip',variables('vms')[copyIndex()])]", + "location": "[parameters('location')]", + "copy": { + "name": "publicIpLoop", + "count": "[length(variables('vms'))]" + }, + "sku": { + "name": "Standard", + "tier": "Regional" + }, + "properties": { + "publicIPAllocationMethod": "Static", + "dnsSettings": { + "domainNameLabel": "[toLower(concat(parameters('VMDnsPrefix'),variables('vms')[copyIndex()]))]" + } + } + }, + { + "type": "Microsoft.Network/networkInterfaces", + "apiVersion": "2018-07-01", + "name": "[concat('nic',variables('vms')[copyIndex()])]", + "location": "[parameters('location')]", + "copy": { + "name": "interfaceLoop", + "count": "[length(variables('vms'))]" + }, + "dependsOn": [ + "[variables('lbName')]", + "[resourceId('Microsoft.Network/publicIPAddresses/',concat('pip',variables('vms')[copyIndex()]))]", + "[resourceId('Microsoft.Network/networkSecurityGroups/',variables('NSGName'))]", + "[variables('virtualNetworkName')]" + ], + "properties": { + "networkSecurityGroup": { + "id": "[resourceId('Microsoft.Network/networkSecurityGroups/',variables('NSGName'))]" + }, + "ipConfigurations": [ + { + "name": "ipconfig1", + "properties": { + "privateIPAllocationMethod": "Static", + "privateIPAddress": "[variables('ips')[copyIndex()]]", + "publicIPAddress": { + "id": "[resourceId('Microsoft.Network/publicIPAddresses/', concat('pip',variables('vms')[copyIndex()]))]" + }, + "subnet": { + "id": "[resourceId('Microsoft.Network/virtualNetworks/subnets',variables('virtualNetworkName'),'Subnet')]" + }, + "loadBalancerBackendAddressPools": "[variables('lbaddrpool')]" + } + } + ] + } + }, + { + "type": "Microsoft.Compute/virtualMachines", + "apiVersion": "2018-06-01", + "name": "[variables('vms')[copyIndex()]]", + "location": "[parameters('location')]", + "copy": { + "name": "vmLoop", + "count": "[length(variables('vms'))]" + }, + "dependsOn": [ + "[resourceId('Microsoft.Storage/storageAccounts/', variables('storageAccountName'))]", + "[resourceId('Microsoft.Network/networkInterfaces/',concat('nic',variables('vms')[copyIndex()]) )]" + ], + "zones": "[split(string(add(mod(copyIndex(),3),1)), ',')]", + "identity": { + "type": "systemAssigned" + }, + "properties": { + "hardwareProfile": { + "vmSize": "[parameters('vmSize')]" + }, + "osProfile": { + "computerName": "[variables('vms')[copyIndex()]]", + "adminUsername": "[variables('adminUsername')]", + "adminPassword": "[parameters('adminPassword')]", + "linuxConfiguration": "[if(equals(variables('ostype'),'linux'),variables('linuxConfiguration'),'')]" + }, + "storageProfile": "[if(equals(parameters('OS'),'Linux CentOS'),variables('CentosStorageProfile'),variables('windowsStorageProfile'))]", + "networkProfile": { + "networkInterfaces": [ + { + "id": "[resourceId('Microsoft.Network/networkInterfaces/', concat('nic',variables('vms')[copyIndex()]))]" + } + ] + }, + "diagnosticsProfile": { + "bootDiagnostics": { + "enabled": true, + "storageUri": "[reference(resourceId('Microsoft.Storage/storageAccounts/', variables('storageAccountName'))).primaryEndpoints.blob]" + } + } + } + }, + { + "condition": "[equals(parameters('azurePwsh'),'yes')]", + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2016-07-01", + "name": "[guid(resourceGroup().id,variables('vms')[copyIndex()])]", + "copy": { + "name": "roleLoop", + "count": "[length(variables('vms'))]" + }, + "dependsOn": [ + "[variables('vms')[copyIndex()]]" + ], + "properties": { + "roleDefinitionId": "[variables('NetworkContributor')]", + "principalId": "[reference(concat('Microsoft.Compute/virtualMachines/', variables('vms')[copyIndex()]), '2018-06-01', 'Full').identity.principalId]" + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2018-05-01", + "name": "[concat('safekitInstall',variables('vms')[copyIndex()])]", + "copy": { + "name": "skCopy", + "count": "[length(variables('vms'))]" + }, + "dependsOn": [ + "[variables('vms')[copyIndex()]]" + ], + "properties": { + "mode": "Incremental", + "templateLink": { + "uri": "[variables('sktemplate')]", + "contentVersion": "1.0.0.0" + }, + "parameters": { + "location": { + "value": "[parameters('location')]" + }, + "vmname": { + "value": "[variables('vms')[copyIndex()]]" + }, + "ostype": { + "value": "[variables('ostype')]" + }, + "azurePwsh": { + "value": "[parameters('azurePwsh')]" + }, + "capassword": { + "value": "[parameters('AdminPassword')]" + }, + "safekitFileUri": { + "value": "[parameters('safekitFileUri')]" + }, + "_artifactsLocation": { + "value": "[parameters('_artifactsLocation')]" + }, + "_artifactsLocationSasToken": { + "value": "[parameters('_artifactsLocationSasToken')]" + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2018-05-01", + "name": "safekitClusterConfig", + "dependsOn": [ + "skCopy" + ], + "properties": { + "mode": "Incremental", + "templateLink": { + "uri": "[variables('skclustertemplate')]", + "contentVersion": "1.0.0.0" + }, + "parameters": { + "location": { + "value": "[parameters('location')]" + }, + "ostype": { + "value": "[variables('ostype')]" + }, + "vmname": { + "value": "[variables('vms')[0]]" + }, + "vmList": { + "value": "[variables('vms')]" + }, + "fqdn": { + "value": "[reference(resourceId('Microsoft.Network/publicIPAddresses/',concat('pip',variables('vms')[0]))).dnsSettings.fqdn]" + }, + "privateIps": { + "value": "[variables('ips')]" + }, + "VMDnsPrefix": { + "value": "[parameters('VMDnsPrefix')]" + }, + "lblist": { + "value": "[if(greater(length(parameters('VIPDnsLabel')),0),reference('optionalVip').outputs.fqdn.value,'')]" + }, + "capassword": { + "value": "[parameters('AdminPassword')]" + }, + "_artifactsLocation": { + "value": "[parameters('_artifactsLocation')]" + }, + "_artifactsLocationSasToken": { + "value": "[parameters('_artifactsLocationSasToken')]" + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2018-05-01", + "name": "safekitModuleConfig", + "dependsOn": [ + "safekitClusterConfig" + ], + "properties": { + "mode": "Incremental", + "templateLink": { + "uri": "[variables('skmoduletemplate')]", + "contentVersion": "1.0.0.0" + }, + "parameters": { + "location": { + "value": "[parameters('location')]" + }, + "ostype": { + "value": "[variables('ostype')]" + }, + "vmname": { + "value": "[variables('vms')[0]]" + }, + "moduleUrl": { + "value": "[parameters('moduleUrl')]" + }, + "moduleName": { + "value": "[parameters('moduleName')]" + }, + "_artifactsLocation": { + "value": "[parameters('_artifactsLocation')]" + }, + "_artifactsLocationSasToken": { + "value": "[parameters('_artifactsLocationSasToken')]" + } + } + } + } + ], + "outputs": { + "adminUser": { + "type": "string", + "value": "[variables('adminUserName')]" + }, + "fqdn": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Network/publicIPAddresses/',concat('pip',variables('vms')[0]))).dnsSettings.fqdn]" + }, + "Credentials Url": { + "type": "string", + "value": "[concat('https://',reference(resourceId('Microsoft.Network/publicIPAddresses/',concat('pip',variables('vms')[0]))).dnsSettings.fqdn,':9001/adduser.html')]" + }, + "Credentials Url Login": { + "type": "string", + "value": "user: CA_admin, password: the value of AdminPassword" + }, + "Console Url": { + "type": "string", + "value": "[concat('https://',reference(resourceId('Microsoft.Network/publicIPAddresses/',concat('pip',variables('vms')[0]))).dnsSettings.fqdn,':9453/deploy.html')]" + }, + "Mosaic URL": { + "type": "string", + "value": "[concat('https://',reference('optionalVip').outputs.fqdn.value,':9453/cgi-bin/mosaic?mode=mosaic&arg0=',parameters('moduleName'))]" + } + } +} \ No newline at end of file diff --git a/safekit-cluster-farm/nestedtemplates/createvip.json b/safekit-cluster-farm/nestedtemplates/createvip.json new file mode 100644 index 000000000000..54a91d4d1b64 --- /dev/null +++ b/safekit-cluster-farm/nestedtemplates/createvip.json @@ -0,0 +1,43 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "VIPDnsLabel": { + "type": "string", + "metadata": { + "description": "Public VIP dns label (optional. If set, an additionnal Standard SKU, unassociated public IP will be created)" + }, + "defaultValue": "" + }, + "location": { + "type": "string", + "metadata": { + "description": "resources location" + } + } + }, + "resources": [ + { + "condition": "[not(empty(parameters('VIPDnsLabel')))]", + "type": "Microsoft.Network/publicIPAddresses", + "apiVersion": "2018-07-01", + "name": "[concat('pip',parameters('VIPDnsLabel'))]", + "location": "[parameters('location')]", + "sku": { + "name": "Standard" + }, + "properties": { + "publicIPAllocationMethod": "Static", + "dnsSettings": { + "domainNameLabel": "[toLower(parameters('VIPDnsLabel'))]" + } + } + } + ], + "outputs": { + "fqdn": { + "type": "string", + "value": "[if(empty(parameters('VIPDnsLabel')),'',reference(concat('pip',parameters('VIPDnsLabel'))).dnsSettings.fqdn)]" + } + } +} \ No newline at end of file diff --git a/safekit-cluster-farm/nestedtemplates/installCluster.json b/safekit-cluster-farm/nestedtemplates/installCluster.json new file mode 100644 index 000000000000..8b3d41f51960 --- /dev/null +++ b/safekit-cluster-farm/nestedtemplates/installCluster.json @@ -0,0 +1,134 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "vmname": { + "type": "string", + "metadata": { + "description": "vm name" + } + }, + "ostype": { + "type": "string", + "metadata": { + "description": "OS type (windows, linux)" + }, + "defaultValue": "windows" + }, + "vmList": { + "type": "array", + "metadata": { + "description": "vm names" + } + }, + "fqdn": { + "type": "string", + "metadata": { + "description": "first vm public ip name" + } + }, + "privateIps": { + "type": "array", + "metadata": { + "description": "vm private ip list" + } + }, + "VMDnsPrefix":{ + "type": "string", + "metadata": { + "description": "Public VM IP dns label prefix" + } + }, + "lblist": { + "type": "string", + "metadata": { + "description": "dns name of loadbalancer" + }, + "defaultValue": "" + }, + "capassword": { + "type": "securestring", + "metadata": { + "description": "password for CA server access" + } + }, + "location": { + "type": "string", + "metadata": { + "description": "resources location" + } + }, + "_artifactsLocation": { + "type": "string", + "metadata": { + "description": "base URL of deployment resources (template,subtemplates,scripts)" + } + }, + "_artifactsLocationSasToken": { + "type": "securestring", + "metadata": { + "description": "The sasToken required to access _artifactsLocation. When the template is deployed using the accompanying scripts, a sasToken will be automatically generated. Use the defaultValue if the staging location is not secured." + } + } + }, + "variables": { + "pwconfscripturi": "[concat(uri(parameters('_artifactsLocation'),'scripts/configCluster.ps1'),parameters('_artifactsLocationSasToken'))]", + "pwcertscripturi": "[concat(uri(parameters('_artifactsLocation'),'scripts/uploadcerts.ps1'),parameters('_artifactsLocationSasToken'))]", + "pwiscripturi": "[concat(uri(parameters('_artifactsLocation'),'scripts/installCluster.ps1'),parameters('_artifactsLocationSasToken'))]", + "shelliscripturi": "[concat(uri(parameters('_artifactsLocation'),'scripts/installCluster.sh'),parameters('_artifactsLocationSasToken'))]", + "domain": "[skip(parameters('fqdn'),indexOf(parameters('fqdn'),'.'))]", + "publicipfmt": "[concat(parameters('VMDnsPrefix'),'%VM%',variables('domain'))]", + "privateiplistarg": "[replace(string(parameters('privateIps')),'\"','')]", + "vmlistarg": "[replace(string(parameters('vmList')),'\"','')]", + "windowsfileuris": [ + "[variables('pwcertscripturi')]", + "[variables('pwconfscripturi')]", + "[variables('pwiscripturi')]" + ], + "linuxfileuris": [ + "[variables('pwcertscripturi')]", + "[variables('pwconfscripturi')]", + "[variables('shelliscripturi')]" + ], + "properties": { + "windows": { + "publisher": "Microsoft.Compute", + "type": "CustomScriptExtension", + "typeHandlerVersion": "1.9", + "autoUpgradeMinorVersion": true, + "settings": { + "fileUris": "[variables('windowsfileuris')]" + }, + "protectedSettings": { + "commandToExecute": "[concat('powershell -ExecutionPolicy Unrestricted -File .\\InstallCluster.ps1 -vmlist \"', variables('vmlistarg'),'\" -publicipfmt \"',variables('publicipfmt'),'\" -privateiplist \"',variables('privateiplistarg'),'\" -lblist \"',parameters('lblist'),'\" -Passwd \"',parameters('capassword'),'\"')]" + } + }, + "linux": { + "publisher": "Microsoft.Azure.Extensions", + "type": "CustomScript", + "typeHandlerVersion": "2.0", + "autoUpgradeMinorVersion": true, + "settings": { + "skipDos2Unix": false, + "timestamp": 1, + "fileUris": "[variables('linuxfileuris')]" + }, + "protectedSettings": { + "commandToExecute": "[concat('sh installCluster.sh \"',variables('vmlistarg'),'\" \"',variables('publicipfmt'),'\" \"',variables('privateiplistarg'),'\" \"',parameters('lblist'),'\" \"',parameters('capassword'),'\"') ]" + } + } + } + }, + "resources": [ + { + "name": "[concat(parameters('vmname'),'/safekit')]", + "type": "Microsoft.Compute/virtualMachines/extensions", + "apiVersion": "2018-06-01", + "location": "[parameters('location')]", + "properties": "[variables('properties')[parameters('ostype')]]" + } + ], + "outputs": { + + } +} \ No newline at end of file diff --git a/safekit-cluster-farm/nestedtemplates/installModule.json b/safekit-cluster-farm/nestedtemplates/installModule.json new file mode 100644 index 000000000000..d925084306b6 --- /dev/null +++ b/safekit-cluster-farm/nestedtemplates/installModule.json @@ -0,0 +1,99 @@ + { + "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "vmname": { + "type": "string", + "metadata": { + "description": "vm name" + } + }, + "ostype":{ + "type": "string", + "metadata": { + "description": "OS type (windows, linux)" + }, + "defaultValue": "windows" + }, + "moduleUrl": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "url of the application module to install on all nodes" + } + }, + "moduleName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "url of the application module to install on all nodes" + } + }, + "location":{ + "type": "string", + "metadata":{ + "description": "resources location" + } + }, + "_artifactsLocation":{ + "type":"string", + "metadata":{ + "description":"base URL of deployment resources (template,subtemplates,scripts)" + } + }, + "_artifactsLocationSasToken": { + "type": "securestring", + "metadata": { + "description": "The sasToken required to access _artifactsLocation. When the template is deployed using the accompanying scripts, a sasToken will be automatically generated. Use the defaultValue if the staging location is not secured." + } + } + }, + "variables":{ + "installModuleuri":"[concat(uri(parameters('_artifactsLocation'),'scripts/installModule.ps1'),parameters('_artifactsLocationSasToken'))]", + "properties" : { + "windows": { + "publisher": "Microsoft.Compute", + "type": "CustomScriptExtension", + "typeHandlerVersion": "1.9", + "autoUpgradeMinorVersion": true, + "settings": { + "fileUris": [ + "[parameters('moduleUrl')]", + "[variables('installModuleuri')]" + ] + }, + "protectedSettings":{ + "commandToExecute": "[concat('powershell -ExecutionPolicy Unrestricted -File .\\installModule.ps1 -safekitcmd C:/safekit/safekit -MName \"',parameters('moduleName'),'\"')]" + } + }, + "linux" : { + "publisher": "Microsoft.Azure.Extensions", + "type": "CustomScript", + "typeHandlerVersion": "2.0", + "autoUpgradeMinorVersion": true, + "settings": { + "skipDos2Unix":false, + "timestamp": 1, + "fileUris": [ + "[parameters('moduleUrl')]", + "[variables('installModuleuri')]" + ] + }, + "protectedSettings":{ + "commandToExecute": "[concat('pwsh ./installModule.ps1 -safekitcmd /opt/safekit/safekit -MName \"',parameters('moduleName'),'\"')]" + } + } + } + }, + "resources":[ + { + "name": "[concat(parameters('vmname'),'/safekit')]", + "type": "Microsoft.Compute/virtualMachines/extensions", + "apiVersion": "2018-06-01", + "location": "[parameters('location')]", + "properties": "[variables('properties')[parameters('ostype')]]" + } + ], + "outputs": {} + } + \ No newline at end of file diff --git a/safekit-cluster-farm/nestedtemplates/safekitdepl.json b/safekit-cluster-farm/nestedtemplates/safekitdepl.json new file mode 100644 index 000000000000..852b489f0d65 --- /dev/null +++ b/safekit-cluster-farm/nestedtemplates/safekitdepl.json @@ -0,0 +1,109 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "vmname": { + "type": "string", + "metadata": { + "description": "vm name" + } + }, + "ostype": { + "type": "string", + "metadata": { + "description": "os type" + }, + "defaultValue": "windows" + }, + "capassword": { + "type": "securestring", + "metadata": { + "description": "password for CA server access" + } + }, + "azurePwsh": { + "type": "string", + "metadata": { + "description": "install azure powershell module (optional)" + }, + "defaultValue": "no", + "allowedValues": [ + "yes", + "no" + ] + }, + "safekitFileUri": { + "type": "string", + "metadata": { + "description": "url of safekit package" + }, + "defaultValue": "" + }, + "location": { + "type": "string", + "metadata": { + "description": "resources location" + } + }, + "_artifactsLocation": { + "type": "string", + "metadata": { + "description": "base URL of deployment resources (template,subtemplates,scripts)" + } + }, + "_artifactsLocationSasToken": { + "type": "securestring", + "metadata": { + "description": "The sasToken required to access _artifactsLocation. When the template is deployed using the accompanying scripts, a sasToken will be automatically generated. Use the defaultValue if the staging location is not secured." + } + } + }, + "variables": { + "pwazrmuri": "[concat(uri(parameters('_artifactsLocation'),'scripts/installAzureRM.ps1'),parameters('_artifactsLocationSasToken'))]", + "pwscripturi": "[concat(uri(parameters('_artifactsLocation'),'scripts/InstallSafeKit.ps1'),parameters('_artifactsLocationSasToken'))]", + "skpkg": "[last(split(parameters('safekitFileUri'),'/'))]", + "shellscripturi": "[concat(uri(parameters('_artifactsLocation'),'scripts/InstallSafeKit.sh'),parameters('_artifactsLocationSasToken'))]", + "skuri": "[parameters('safekitFileUri')]", + "fileUrisW": "[if(equals(parameters('azurePwsh'),'yes'),createArray(variables('skuri'),variables('pwscripturi'),variables('pwazrmuri')),createArray(variables('skuri'),variables('pwscripturi')))]", + "fileUrisL": "[if(equals(parameters('azurePwsh'),'yes'),createArray(variables('skuri'),variables('shellscripturi'),variables('pwazrmuri')),createArray(variables('skuri'),variables('shellscripturi')))]", + "properties": { + "windows": { + "publisher": "Microsoft.Compute", + "type": "CustomScriptExtension", + "typeHandlerVersion": "1.9", + "autoUpgradeMinorVersion": true, + "settings": { + "fileUris": "[variables('fileUrisW')]" + }, + "protectedSettings":{ + "commandToExecute": "[concat('powershell -ExecutionPolicy Unrestricted -File .\\InstallSafekit.ps1 -SkFile ',variables('skpkg'),' -Passwd \"',parameters('capassword'),'\"')]" + } + }, + "linux": { + "publisher": "Microsoft.Azure.Extensions", + "type": "CustomScript", + "typeHandlerVersion": "2.0", + "autoUpgradeMinorVersion": true, + "settings": { + "skipDos2Unix": false, + "fileUris": "[variables('fileUrisL')]" + }, + "protectedSettings": { + "commandToExecute": "[concat('sh InstallSafeKit.sh ./', variables('skpkg'), ' \"',parameters('capassword'),'\"')]" + } + } + } + }, + "resources": [ + { + "name": "[concat(parameters('vmname'),'/safekit')]", + "type": "Microsoft.Compute/virtualMachines/extensions", + "apiVersion": "2018-06-01", + "location": "[parameters('location')]", + "properties": "[variables('properties')[parameters('ostype')]]" + } + ], + "outputs": { + + } +} \ No newline at end of file diff --git a/safekit-cluster-farm/scripts/InstallSafeKit.ps1 b/safekit-cluster-farm/scripts/InstallSafeKit.ps1 new file mode 100644 index 000000000000..39c056ba2a29 --- /dev/null +++ b/safekit-cluster-farm/scripts/InstallSafeKit.ps1 @@ -0,0 +1,61 @@ +param( + [string] $SkFile, + [string] $Passwd +) + +function Log { + param( + [string] $m + ) + + $Stamp = (Get-Date).toString("yyyy/MM/dd HH:mm:ss") + Add-Content ./installsk.log "$stamp [InstallSafeKit.ps1] $m" +} + +Log $vmlist +Log $modname + +if( ! (Test-Path -Path "/safekit" )) { + +if( ! (Test-Path -Path "$skFile" )){ + + Log "Download $SkFile failed. Check calling template fileUris property." + exit -1 +} + +Log "Installing ..." +$arglist = @( + "/i", + "$SkFile", + "/qn", + "/l*vx", + "loginst.txt", + "DODESKTOP='0'" +) + +Start-Process msiexec.exe -ArgumentList $arglist -Wait +Log "Install Azure RM" + +if(Test-Path -Path "./installAzureRm.ps1") { + & ./installAzureRm.ps1 +} + +Log "Applying firewall rules" +& \safekit\private\bin\firewallcfg.cmd add + +Log "Starting CA helper service" +$cwd = Get-Location +try{ + cd /safekit/web/bin + & ./startcaserv.cmd "$Passwd" +}finally{ + set-location $cwd +} + +} +else{ + Log "safekit already installed" +} +Log "end of script" + + diff --git a/safekit-cluster-farm/scripts/InstallSafeKit.sh b/safekit-cluster-farm/scripts/InstallSafeKit.sh new file mode 100644 index 000000000000..2a7a0ad3defc --- /dev/null +++ b/safekit-cluster-farm/scripts/InstallSafeKit.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +echo "Install SafeKit from `ls ./*.bin`" + + +chmod +x ./safekit*.bin +./safekit*.bin + + +yum -y localinstall ./safekit*.rpm +#rm ./safekit*.bin ./safekit*.rpm + +echo "Install powershell" +# Register the Microsoft RedHat repository +curl https://packages.microsoft.com/config/rhel/7/prod.repo | sudo tee /etc/yum.repos.d/microsoft.repo +yum update powershell +# Install PowerShell +yum install -y powershell +if [ -f "installAzureRM.ps1" ]; then + pwsh ./installAzureRM.ps1 -linux +fi +echo "starting CA helper service" +cd /opt/safekit/web/bin +./startcaserv "$2" \ No newline at end of file diff --git a/safekit-cluster-farm/scripts/UpdateSafeKit.sh b/safekit-cluster-farm/scripts/UpdateSafeKit.sh new file mode 100644 index 000000000000..a97b094a5c4b --- /dev/null +++ b/safekit-cluster-farm/scripts/UpdateSafeKit.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +/opt/safekit/safekit shutdown +/opt/safekit/safekit uninstall + +echo "Install SafeKit" +yum -y localinstall $1 diff --git a/safekit-cluster-farm/scripts/cfgFarm.ps1 b/safekit-cluster-farm/scripts/cfgFarm.ps1 new file mode 100644 index 000000000000..06d0e111b8b2 --- /dev/null +++ b/safekit-cluster-farm/scripts/cfgFarm.ps1 @@ -0,0 +1,39 @@ +param( + [string] $safekitcmd, + [string] $safekitmod, + [string] $MName +) + + + +function Log { + param( + [string] $m + ) + + $Stamp = (Get-Date).toString("yyyy/MM/dd HH:mm:ss") + Add-Content ./installsk.log "$stamp $m" +} + +Log $safekitcmd +Log $MName + +if ($MName){ + + $ucfg = [Xml] (Get-Content "$safekitmod/$MName/conf/userconfig.xml") + $ucfg.safe.service.farm.lan.name="default" + + + $ucfg.Save("$safekitmod/$MName/conf/userconfig.xml") + Log "$ucfg.OuterXml" + + + $res = & $safekitcmd -H "*" -E $MName + Log "$MName => $res" + + & $safekitcmd -H "*" start -m $MName +} + +Log "end of script" + + diff --git a/safekit-cluster-farm/scripts/configCluster.ps1 b/safekit-cluster-farm/scripts/configCluster.ps1 new file mode 100644 index 000000000000..dace9f62f759 --- /dev/null +++ b/safekit-cluster-farm/scripts/configCluster.ps1 @@ -0,0 +1,88 @@ +param( + [string] $publicipfmt, + [string] $privateiplist, + [string] $vmlist, + [string] $lblist, + [string] $Passwd +) + +$safekitcmd=$env:SAFEKITCMD +$safevar=$env:SAFEVAR +$safewebconf=$env:SAFEWEBCONF +$logdir=$pwd + +function Log { + param( + [string] $m + ) + + $Stamp = (Get-Date).toString("yyyy/MM/dd HH:mm:ss") + Add-Content "$logdir/installsk.log" "$stamp [configCluster.ps1] $m" +} + +Log $vmlist +Log $publicipfmt +Log $privateiplist +Log $lblist + +if ($vmlist){ + $vmargs=@() + $lbargs=@() + $privateipargs=@() + $targets=@() + + "[" | Out-File -Encoding ASCII -FilePath "$safewebconf/ipnames.json" + "[" | Out-File -Encoding ASCII -FilePath "$safewebconf/ipv4.json" + + + $vmargs += ([regex]::Replace($vmlist,'[\[\]]','') -split ',') + $privateipargs += ([regex]::Replace($privateiplist,'[\[\]]','') -split ',') + if($lblist){ + $lbargs += ($lblist -split ',') + } + Log "configuring cluster.xml and certificates input files" + + $str = "" + if($publicipfmt){ + $str +="" + + for ($i=0; $i -lt $vmargs.Length; $i++){ + $dnsname=$($publicipfmt).Replace('%VM%',$($vmargs[$i])).ToLower() + $str += "" + "`"$dnsname`"," | Out-File -Append -Encoding ASCII -FilePath "$safewebconf/ipnames.json" + } + + $str += "" + } + + for($i=0; $i -lt $lbargs.Length; $i++){ + $dnsname = $($lbargs[$i]) + if($dnsname.Length){ + "`"$dnsname`"," | Out-File -Append -Encoding ASCII -FilePath "$safewebconf/ipnames.json" + } + } + "null]" | Out-File -Append -Encoding ASCII -FilePath "$safewebconf/ipnames.json" + + + $str +="" + for ($i=0; $i -lt $vmargs.Length; $i++){ + $str += "" + "`"$($privateipargs[$i])`"," | Out-File -Append -Encoding ASCII -FilePath "$safewebconf/ipv4.json" + $targets += $($privateipargs[$i]) + } + "null]" | Out-File -Append -Encoding ASCII -FilePath "$safewebconf/ipv4.json" + + $str += "" + $str | Out-File -Encoding utf8 $safevar\cluster\cluster.xml + & $safekitcmd cluster config 2>&1 + $res= & $safekitcmd -H "[http],*" -G 2>&1 + Log "result = $res" + if( Test-Path "./uploadcerts.ps1") { + & ./uploadcerts.ps1 -skbase "$env:SAFEBASE" -targets $targets -userpwd "CA_admin:$Passwd" + } +} + +Log "end of script" + + + diff --git a/safekit-cluster-farm/scripts/installAzureRM.ps1 b/safekit-cluster-farm/scripts/installAzureRM.ps1 new file mode 100644 index 000000000000..e808b410004b --- /dev/null +++ b/safekit-cluster-farm/scripts/installAzureRM.ps1 @@ -0,0 +1,17 @@ +param( + [switch] $linux=$false +) + +if ( $linux ) { + +Install-Module AzureRM.NetCore -SkipPublisherCheck -Force +Import-Module AzureRM.Netcore + +}else{ + +Install-PackageProvider -name Nuget -MinimumVersion 2.8.5.201 -Force +Install-Module AzureRM -SkipPublisherCheck -Force + +Import-Module AzureRM + +} \ No newline at end of file diff --git a/safekit-cluster-farm/scripts/installCluster.ps1 b/safekit-cluster-farm/scripts/installCluster.ps1 new file mode 100644 index 000000000000..270c680fdadd --- /dev/null +++ b/safekit-cluster-farm/scripts/installCluster.ps1 @@ -0,0 +1,33 @@ +param( + [string] $publicipfmt, + [string] $privateiplist, + [string] $vmlist, + [string] $lblist, + [string] $Passwd +) + +$targetDir = "." + +function Log { + param( + [string] $m + ) + + $Stamp = (Get-Date).toString("yyyy/MM/dd HH:mm:ss") + Add-Content ./installsk.log "$stamp [installCluster.ps1] $m" +} + +Log $vmlist +Log $publicipfmt +Log $privateiplist + +$env:SAFEBASE="/safekit" +$env:SAFEKITCMD="/safekit/safekit.exe" +$env:SAFEVAR="/safekit/var" +$env:SAFEWEBCONF="/safekit/web/conf" + +& ./configCluster.ps1 -vmlist $vmlist -publicipfmt $publicipfmt -privateiplist $privateiplist -lblist $lblist -Passwd $Passwd + +Log "end of script" + + diff --git a/safekit-cluster-farm/scripts/installCluster.sh b/safekit-cluster-farm/scripts/installCluster.sh new file mode 100644 index 000000000000..b02640e0a772 --- /dev/null +++ b/safekit-cluster-farm/scripts/installCluster.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +echo $* + +# Start PowerShell config script +export SAFEKITCMD="/opt/safekit/safekit" +export SAFEVAR="/var/safekit" +export SAFEWEBCONF="/opt/safekit/web/conf" +export SAFEBASE="/opt/safekit" +export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/opt/safekit/private/bin" +pwsh ./configCluster.ps1 -vmlist "$1" -publicipfmt "$2" -privateiplist "$3" -lblist "$4" -Passwd "$5" diff --git a/safekit-cluster-farm/scripts/installModule.ps1 b/safekit-cluster-farm/scripts/installModule.ps1 new file mode 100644 index 000000000000..890150f36e4c --- /dev/null +++ b/safekit-cluster-farm/scripts/installModule.ps1 @@ -0,0 +1,31 @@ +param( + [string]$safekitcmd, + [string]$MName, + [string]$modulepkg, + [string]$modulecfgscript +) + +if( $modulepkg ){ + $module = $modulepkg.Split(',') | Get-ChildItem +} +else{ + $module = [array] (Get-ChildItem "*.safe") +} + +if($module.Length){ + $module[0] | %{ + if($_){ + if($MName -and ($($MName.Length) -gt 0)) { + $modulename=$MName + }else{ + $modulename = $($_.name.Replace(".safe","")) + } + + & $safekitcmd module install -m $modulename $_.fullname + if($modulecfgscript -and (Test-Path "./$modulecfgscript")){ + & ./$modulecfgscript + } + & $safekitcmd -H "*" -E $modulename + } + } +} \ No newline at end of file diff --git a/safekit-cluster-farm/scripts/uploadcerts.ps1 b/safekit-cluster-farm/scripts/uploadcerts.ps1 new file mode 100644 index 000000000000..6201a6353834 --- /dev/null +++ b/safekit-cluster-farm/scripts/uploadcerts.ps1 @@ -0,0 +1,47 @@ +param( + [string[]] $targets, + [string] $userpwd="CA_admin:CA_admin", + [string] $skbase="/safekit" +) + +$curlcmd="$skbase/private/bin/curl.exe" +$safeweb="$skbase/web" + +if (Test-Path "$safeweb/conf/ca"){ + Write-Host "CA already initialized, skipping certificate generation" +}else{ +$caname = "/CN=Safekit CA" + +try{ + $meta = Invoke-RestMethod -Headers @{"Metadata"="true"} -URI "http://169.254.169.254/metadata/instance?api-version=2017-08-01" -Method get + if($meta) { + $caname = "/CN=SafeKit CA for Azure $($meta.compute.resourceGroupName) cluster" + } +}catch{} + +$cwd = Get-Location +try{ + cd "$safeweb/bin" + & ./initssl sca "$caname" + & $curlcmd "-k" "-u" "$userpwd" "-X" "POST" "-F" "action=swhttps" "https://localhost:9001/caserv" +}finally{ + cd $cwd +} +} + +for($i=1; $i -lt $targets.Length; $i++) { + $targetip = $($targets[$i]) + + Write-Host "uploading cert to $targetip" + + & $curlcmd "-k" "-u" "$userpwd" "-X" "POST" "-F" "file=@$safeweb/conf/cacert.crt" "-F" "action=import" "-F" "target=T_CA" "-F" "add=yes" "https://$($targetip):9001/caserv" + & $curlcmd "-k" "-u" "$userpwd" "-X" "POST" "-F" "file=@$safeweb/conf/cacert.crt" "-F" "action=import" "-F" "target=T_CCA" "-F" "add=yes" "https://$($targetip):9001/caserv" + & $curlcmd "-k" "-u" "$userpwd" "-X" "POST" "-F" "file=@$safeweb/conf/server.crt" "-F" "action=import" "-F" "target=T_SC" "-F" "add=yes" "https://$($targetip):9001/caserv" + & $curlcmd "-k" "-u" "$userpwd" "-X" "POST" "-F" "file=@$safeweb/conf/server.key" "-F" "action=import" "-F" "target=T_SK" "-F" "add=yes" "https://$($targetip):9001/caserv" + & $curlcmd "-k" "-u" "$userpwd" "-X" "POST" "-F" "file=@$safeweb/conf/admin.crt" "-F" "action=import" "-F" "target=T_CC" "-F" "add=yes" "https://$($targetip):9001/caserv" + & $curlcmd "-k" "-u" "$userpwd" "-X" "POST" "-F" "file=@$safeweb/conf/admin.key" "-F" "action=import" "-F" "target=T_CK" "-F" "add=yes" "https://$($targetip):9001/caserv" + & $curlcmd "-k" "-u" "$userpwd" "-X" "POST" "-F" "file=@$safeweb/conf/proxy.crtkey" "-F" "action=import" "-F" "target=T_PCCK" "-F" "add=yes" "https://$($targetip):9001/caserv" + & $curlcmd "-k" "-u" "$userpwd" "-X" "POST" "-F" "file=@$safeweb/conf/sslclient.crl" "-F" "action=import" "-F" "target=T_CRL" "-F" "add=yes" "https://$($targetip):9001/caserv" + & $curlcmd "-k" "-u" "$userpwd" "-X" "POST" "-F" "action=swhttps" "https://$($targetip):9001/caserv" + & $curlcmd "-k" "-u" "$userpwd" "-X" "POST" "-F" "action=shutdown" "https://$($targetip):9001/caserv" +} \ No newline at end of file diff --git a/sccm-technicalpreview/scripts/InstallADDS.ps1 b/sccm-technicalpreview/scripts/InstallADDS.ps1 index e3b53695bd9f..6086bbd78a55 100644 --- a/sccm-technicalpreview/scripts/InstallADDS.ps1 +++ b/sccm-technicalpreview/scripts/InstallADDS.ps1 @@ -67,6 +67,6 @@ else "[$(Get-Date -format HH:mm:ss)] AD have already installed" | Out-File -Append $logpath if(TestADDSForeastInstall -DomainFullName $DomainFullName -eq "Success") { - $result = InstallADDSForest -DomainFullName $DomainFullName + $result = InstallADDSForest -DomainFullName $DomainFullName } } \ No newline at end of file diff --git a/sccm-technicalpreview/scripts/InstallADK.ps1 b/sccm-technicalpreview/scripts/InstallADK.ps1 index 489aa26a340f..b78f809fb99b 100644 --- a/sccm-technicalpreview/scripts/InstallADK.ps1 +++ b/sccm-technicalpreview/scripts/InstallADK.ps1 @@ -18,15 +18,15 @@ $arg4 = "/q" try { - "[$(Get-Date -format HH:mm:ss)] Installing ADK..." | Out-File -Append $logpath - & $cmd $arg1 $arg2 $arg3 $arg4 | out-null - "[$(Get-Date -format HH:mm:ss)] ADK Installed Successfully!" | Out-File -Append $logpath + "[$(Get-Date -format HH:mm:ss)] Installing ADK..." | Out-File -Append $logpath + & $cmd $arg1 $arg2 $arg3 $arg4 | out-null + "[$(Get-Date -format HH:mm:ss)] ADK Installed Successfully!" | Out-File -Append $logpath } catch { - "[$(Get-Date -format HH:mm:ss)] Failed to install ADK with below error:" | Out-File -Append $logpath - $ErrorMessage = $_.Exception.Message - $ErrorMessage | Out-File -Append $logpath + "[$(Get-Date -format HH:mm:ss)] Failed to install ADK with below error:" | Out-File -Append $logpath + $ErrorMessage = $_.Exception.Message + $ErrorMessage | Out-File -Append $logpath } #ADK add-on (17763) @@ -41,13 +41,13 @@ $arg3 = "/q" try { - "[$(Get-Date -format HH:mm:ss)] Installing add-on for ADK..." | Out-File -Append $logpath - & $cmd $arg1 $arg2 $arg3 | out-null - "[$(Get-Date -format HH:mm:ss)] Add-on for ADK Installed Successfully!" | Out-File -Append $logpath + "[$(Get-Date -format HH:mm:ss)] Installing add-on for ADK..." | Out-File -Append $logpath + & $cmd $arg1 $arg2 $arg3 | out-null + "[$(Get-Date -format HH:mm:ss)] Add-on for ADK Installed Successfully!" | Out-File -Append $logpath } catch { - "[$(Get-Date -format HH:mm:ss)] Failed to install Add-on for ADK with below error:" | Out-File -Append $logpath - $ErrorMessage = $_.Exception.Message - $ErrorMessage | Out-File -Append $logpath + "[$(Get-Date -format HH:mm:ss)] Failed to install Add-on for ADK with below error:" | Out-File -Append $logpath + $ErrorMessage = $_.Exception.Message + $ErrorMessage | Out-File -Append $logpath } \ No newline at end of file diff --git a/sccm-technicalpreview/scripts/InstallDP.ps1 b/sccm-technicalpreview/scripts/InstallDP.ps1 index 078ea23d7247..1298335daf6c 100644 --- a/sccm-technicalpreview/scripts/InstallDP.ps1 +++ b/sccm-technicalpreview/scripts/InstallDP.ps1 @@ -7,19 +7,19 @@ if(!(Test-Path $ProvisionToolPath)) } $logpath = $ProvisionToolPath+"\InstallDPLog.txt" $SiteCode = $PSSiteCode # Site code - + $ProviderMachineName = $env:COMPUTERNAME+"."+$DomainFullName # SMS Provider machine name # Customizations $initParams = @{} if($ENV:SMS_ADMIN_UI_PATH -eq $null) { - $ENV:SMS_ADMIN_UI_PATH = "C:\Program Files (x86)\Microsoft Configuration Manager\AdminConsole\bin\i386" + $ENV:SMS_ADMIN_UI_PATH = "C:\Program Files (x86)\Microsoft Configuration Manager\AdminConsole\bin\i386" } # Import the ConfigurationManager.psd1 module if((Get-Module ConfigurationManager) -eq $null) { - Import-Module "$($ENV:SMS_ADMIN_UI_PATH)\..\ConfigurationManager.psd1" @initParams + Import-Module "$($ENV:SMS_ADMIN_UI_PATH)\..\ConfigurationManager.psd1" @initParams } # Connect to the site's drive if it is not already present @@ -28,9 +28,9 @@ if((Get-Module ConfigurationManager) -eq $null) { New-PSDrive -Name $SiteCode -PSProvider CMSite -Root $ProviderMachineName @initParams while((Get-PSDrive -Name $SiteCode -PSProvider CMSite -ErrorAction SilentlyContinue) -eq $null) { - "[$(Get-Date -format HH:mm:ss)] Failed ,retry in 10s. Please wait." | Out-File -Append $logpath - Start-Sleep -Seconds 10 - New-PSDrive -Name $SiteCode -PSProvider CMSite -Root $ProviderMachineName @initParams + "[$(Get-Date -format HH:mm:ss)] Failed ,retry in 10s. Please wait." | Out-File -Append $logpath + Start-Sleep -Seconds 10 + New-PSDrive -Name $SiteCode -PSProvider CMSite -Root $ProviderMachineName @initParams } # Set the current location to be the site code. @@ -39,7 +39,7 @@ Set-Location "$($SiteCode):\" @initParams $DPServerFullName = $DPServerName + "." + $DomainFullName if($(Get-CMSiteSystemServer -SiteSystemServerName $DPServerFullName) -eq $null) { - New-CMSiteSystemServer -Servername $DPServerFullName -Sitecode $SiteCode + New-CMSiteSystemServer -Servername $DPServerFullName -Sitecode $SiteCode } $Date = [DateTime]::Now.AddYears(10) diff --git a/sccm-technicalpreview/scripts/InstallFeature.ps1 b/sccm-technicalpreview/scripts/InstallFeature.ps1 index d7c07ad8ebfc..6b1bdbb9b2a3 100644 --- a/sccm-technicalpreview/scripts/InstallFeature.ps1 +++ b/sccm-technicalpreview/scripts/InstallFeature.ps1 @@ -76,11 +76,11 @@ if($rolelist -contains "Management point") } if($rolelist -contains "Reporting services point") { - #installed .net 4.5 or later + #installed .net 4.5 or later } if($rolelist -contains "Service connection point") { - #installed .net 4.5 or later + #installed .net 4.5 or later } if($rolelist -contains "Software update point") { diff --git a/sccm-technicalpreview/scripts/InstallMP.ps1 b/sccm-technicalpreview/scripts/InstallMP.ps1 index 4042e1c9d3c5..3ea6e2c18363 100644 --- a/sccm-technicalpreview/scripts/InstallMP.ps1 +++ b/sccm-technicalpreview/scripts/InstallMP.ps1 @@ -9,19 +9,19 @@ if(!(Test-Path $ProvisionToolPath)) } $logpath = $ProvisionToolPath+"\InstallMPLog.txt" $SiteCode = $PSSiteCode # Site code - + $ProviderMachineName = $env:COMPUTERNAME+"."+$DomainFullName # SMS Provider machine name # Customizations $initParams = @{} if($ENV:SMS_ADMIN_UI_PATH -eq $null) { - $ENV:SMS_ADMIN_UI_PATH = "C:\Program Files (x86)\Microsoft Configuration Manager\AdminConsole\bin\i386" + $ENV:SMS_ADMIN_UI_PATH = "C:\Program Files (x86)\Microsoft Configuration Manager\AdminConsole\bin\i386" } # Import the ConfigurationManager.psd1 module if((Get-Module ConfigurationManager) -eq $null) { - Import-Module "$($ENV:SMS_ADMIN_UI_PATH)\..\ConfigurationManager.psd1" @initParams + Import-Module "$($ENV:SMS_ADMIN_UI_PATH)\..\ConfigurationManager.psd1" @initParams } # Connect to the site's drive if it is not already present @@ -30,9 +30,9 @@ if((Get-Module ConfigurationManager) -eq $null) { New-PSDrive -Name $SiteCode -PSProvider CMSite -Root $ProviderMachineName @initParams while((Get-PSDrive -Name $SiteCode -PSProvider CMSite -ErrorAction SilentlyContinue) -eq $null) { - "[$(Get-Date -format HH:mm:ss)] Failed ,retry in 10s. Please wait." | Out-File -Append $logpath - Start-Sleep -Seconds 10 - New-PSDrive -Name $SiteCode -PSProvider CMSite -Root $ProviderMachineName @initParams + "[$(Get-Date -format HH:mm:ss)] Failed ,retry in 10s. Please wait." | Out-File -Append $logpath + Start-Sleep -Seconds 10 + New-PSDrive -Name $SiteCode -PSProvider CMSite -Root $ProviderMachineName @initParams } # Set the current location to be the site code. @@ -42,7 +42,7 @@ $MPServerFullName = $MPServerName + "." + $DomainFullName if($(Get-CMSiteSystemServer -SiteSystemServerName $MPServerFullName) -eq $null) { - New-CMSiteSystemServer -Servername $MPServerFullName -Sitecode $SiteCode + New-CMSiteSystemServer -Servername $MPServerFullName -Sitecode $SiteCode } $DomainName = $DomainFullName.split('.')[0] $DName = $DomainName + "\" + $DomainAdminName @@ -50,16 +50,16 @@ $DName = $DomainName + "\" + $DomainAdminName $pwd = $Password | ConvertTo-SecureString -AsPlainText -Force while($(Get-CMAccount -UserName $DName) -eq $null) { - "[$(Get-Date -format HH:mm:ss)] New CM Account $DomainAdminName." | Out-File -Append $logpath - New-CMAccount -UserName $DName -Password $pwd - Start-Sleep -Seconds 10 + "[$(Get-Date -format HH:mm:ss)] New CM Account $DomainAdminName." | Out-File -Append $logpath + New-CMAccount -UserName $DName -Password $pwd + Start-Sleep -Seconds 10 } $SQLServerFqdnName = "$SQLServerName.$env:userdnsdomain" if($SQLInstanceName -eq "MSSQLSERVER") { - Add-CMManagementPoint -SiteSystemServerName $MPServerFullName -SiteCode $SiteCode -ClientConnectionType InternetAndIntranet -AllowDevice -GenerateAlert -SQLServerFqdnName $SQLServerFqdnName -DatabaseName $DBName -UserName $DName + Add-CMManagementPoint -SiteSystemServerName $MPServerFullName -SiteCode $SiteCode -ClientConnectionType InternetAndIntranet -AllowDevice -GenerateAlert -SQLServerFqdnName $SQLServerFqdnName -DatabaseName $DBName -UserName $DName } else { - Add-CMManagementPoint -SiteSystemServerName $MPServerFullName -SiteCode $SiteCode -ClientConnectionType InternetAndIntranet -AllowDevice -GenerateAlert -SQLServerFqdnName $SQLServerFqdnName -SQLServerInstanceName $SQLInstanceName -DatabaseName $DBName -UserName $DName + Add-CMManagementPoint -SiteSystemServerName $MPServerFullName -SiteCode $SiteCode -ClientConnectionType InternetAndIntranet -AllowDevice -GenerateAlert -SQLServerFqdnName $SQLServerFqdnName -SQLServerInstanceName $SQLInstanceName -DatabaseName $DBName -UserName $DName } \ No newline at end of file diff --git a/sccm-technicalpreview/scripts/InstallSCCM.ps1 b/sccm-technicalpreview/scripts/InstallSCCM.ps1 index 1280d969fd44..cceda5db2309 100644 --- a/sccm-technicalpreview/scripts/InstallSCCM.ps1 +++ b/sccm-technicalpreview/scripts/InstallSCCM.ps1 @@ -20,16 +20,16 @@ $AzcopyPath = "C:\Program Files (x86)\Microsoft SDKs\Azure\AzCopy" if(!(Test-Path $AzcopyPath)) { - $path = "$ProvisionToolPath\azcopy.msi" - if(!(Test-Path $path)) - { - #Download azcopy - $url = "http://aka.ms/downloadazcopy" - Invoke-WebRequest -Uri $url -OutFile $path - } - - #Install azcopy - Start-Process msiexec.exe -Wait -ArgumentList "/I $path /quiet" + $path = "$ProvisionToolPath\azcopy.msi" + if(!(Test-Path $path)) + { + #Download azcopy + $url = "http://aka.ms/downloadazcopy" + Invoke-WebRequest -Uri $url -OutFile $path + } + + #Install azcopy + Start-Process msiexec.exe -Wait -ArgumentList "/I $path /quiet" } $cmpath = "c:\"+$CM+".exe" @@ -40,7 +40,7 @@ if(Test-Path $cmpath) } "[$(Get-Date -format HH:mm:ss)] Copying SCCM installation source..." | Out-File -Append $logpath -$cmurl = "http://download.microsoft.com/download/D/8/E/D8E795CE-44D7-40B7-9067-D3D1313865E5/SC_Configmgr_SCEP_TechPreview1810.exe" +$cmurl = "https://go.microsoft.com/fwlink/?linkid=2077212&clcid=0x409" Invoke-WebRequest -Uri $cmurl -OutFile $cmpath if(Test-Path $cmsourcepath) @@ -113,12 +113,12 @@ SysCenterId= if($SQLInstanceName.ToUpper() -eq "MSSQLSERVER") { - $cmini = $cmini.Replace('%SQLInstance%',"") + $cmini = $cmini.Replace('%SQLInstance%',"") } else { - $tinstance = $SQLInstanceName.ToUpper() + "\" - $cmini = $cmini.Replace('%SQLInstance%',$tinstance) + $tinstance = $SQLInstanceName.ToUpper() + "\" + $cmini = $cmini.Replace('%SQLInstance%',$tinstance) } $CMInstallationFile = "c:\" + $CM + "\SMSSETUP\BIN\X64\Setup.exe" $cmini > $CMINIPath diff --git a/sccm-technicalpreview/scripts/JoinDomain.ps1 b/sccm-technicalpreview/scripts/JoinDomain.ps1 index 7004d84e2f4d..8a3d6d3bc018 100644 --- a/sccm-technicalpreview/scripts/JoinDomain.ps1 +++ b/sccm-technicalpreview/scripts/JoinDomain.ps1 @@ -13,8 +13,8 @@ if($DCIPaddress -eq "") } else { - "[$(Get-Date -format HH:mm:ss)] Set DNS to $DCIPaddress" | Out-File -Append $logpath - Set-DnsClientServerAddress -InterfaceIndex $dnsset.InterfaceIndex -ServerAddresses $DCIPaddress + "[$(Get-Date -format HH:mm:ss)] Set DNS to $DCIPaddress" | Out-File -Append $logpath + Set-DnsClientServerAddress -InterfaceIndex $dnsset.InterfaceIndex -ServerAddresses $DCIPaddress } $DomainName = $DomainFullName.split('.')[0] $DName = $DomainName + "\" + $DomainAdminName @@ -27,7 +27,7 @@ $credential = New-Object System.Management.Automation.PSCredential($DName,$pwd) try { Add-Computer -DomainName $DomainFullName -Credential $credential - "[$(Get-Date -format HH:mm:ss)] Finished!" | Out-File -Append $logpath + "[$(Get-Date -format HH:mm:ss)] Finished!" | Out-File -Append $logpath } catch { diff --git a/sccm-technicalpreview/scripts/OpenFirewallPort.ps1 b/sccm-technicalpreview/scripts/OpenFirewallPort.ps1 index 7bea3fc7633a..222d33a71b6d 100644 --- a/sccm-technicalpreview/scripts/OpenFirewallPort.ps1 +++ b/sccm-technicalpreview/scripts/OpenFirewallPort.ps1 @@ -8,8 +8,8 @@ $logpath = $ProvisionToolPath+"\OpenFirewallPortLog.txt" if($rolelist -contains "DC") { #HTTP(S) Requests - New-NetFirewallRule -DisplayName 'HTTP(S) Outbound' -Profile Any -Direction Outbound -Action Allow -Protocol TCP -LocalPort @(80,443) -Group "For DC" - New-NetFirewallRule -DisplayName 'HTTP(S) Inbound' -Profile Any -Direction Inbound -Action Allow -Protocol TCP -LocalPort @(80,443) -Group "For DC" + New-NetFirewallRule -DisplayName 'HTTP(S) Outbound' -Profile Any -Direction Outbound -Action Allow -Protocol TCP -LocalPort @(80,443) -Group "For DC" + New-NetFirewallRule -DisplayName 'HTTP(S) Inbound' -Profile Any -Direction Inbound -Action Allow -Protocol TCP -LocalPort @(80,443) -Group "For DC" #PS-->DC(in) New-NetFirewallRule -DisplayName 'LDAP Inbound' -Profile Any -Direction Inbound -Action Allow -Protocol TCP -LocalPort 389 -Group "For DC" @@ -23,19 +23,19 @@ if($rolelist -contains "DC") New-NetFirewallRule -DisplayName 'RPC Inbound' -Profile Any -Direction Inbound -Action Allow -Protocol TCP -LocalPort 1024-65535 -Group "For DC" #THAgent - Enable-NetFirewallRule -DisplayGroup "Windows Management Instrumentation (WMI)" -Direction Inbound - Enable-NetFirewallRule -DisplayGroup "File and Printer Sharing" + Enable-NetFirewallRule -DisplayGroup "Windows Management Instrumentation (WMI)" -Direction Inbound + Enable-NetFirewallRule -DisplayGroup "File and Printer Sharing" } if($rolelist -contains "Site Server") { New-NetFirewallRule -DisplayName 'HTTP(S) Outbound' -Profile Any -Direction Outbound -Action Allow -Protocol TCP -LocalPort @(80,443) -Group "For SCCM" - New-NetFirewallRule -DisplayName 'HTTP(S) Inbound' -Profile Any -Direction Inbound -Action Allow -Protocol TCP -LocalPort @(80,443) -Group "For SCCM" + New-NetFirewallRule -DisplayName 'HTTP(S) Inbound' -Profile Any -Direction Inbound -Action Allow -Protocol TCP -LocalPort @(80,443) -Group "For SCCM" #site server<->site server New-NetFirewallRule -DisplayName 'SMB Inbound' -Profile Any -Direction Inbound -Action Allow -Protocol TCP -LocalPort 445 -Group "For SCCM" New-NetFirewallRule -DisplayName 'SMB Outbound' -Profile Any -Direction Outbound -Action Allow -Protocol TCP -LocalPort 445 -Group "For SCCM" - New-NetFirewallRule -DisplayName 'PPTP Inbound' -Profile Any -Direction Inbound -Action Allow -Protocol TCP -LocalPort 1723 -Group "For SCCM" + New-NetFirewallRule -DisplayName 'PPTP Inbound' -Profile Any -Direction Inbound -Action Allow -Protocol TCP -LocalPort 1723 -Group "For SCCM" New-NetFirewallRule -DisplayName 'PPTP Outbound' -Profile Any -Direction Outbound -Action Allow -Protocol TCP -LocalPort 1723 -Group "For SCCM" #priary site server(out) ->DC @@ -66,12 +66,12 @@ if($rolelist -contains "Software Update Point") New-NetFirewallRule -DisplayName 'SMB SUPInbound' -Profile Any -Direction Inbound -Action Allow -Protocol TCP -LocalPort 445 -Group "For SCCM SUP" New-NetFirewallRule -DisplayName 'SMB SUP Outbound' -Profile Any -Direction Outbound -Action Allow -Protocol TCP -LocalPort 445 -Group "For SCCM SUP" New-NetFirewallRule -DisplayName 'HTTP(S) SUP Outbound' -Profile Any -Direction Outbound -Action Allow -Protocol TCP -LocalPort @(8530,8531) -Group "For SCCM SUP" - New-NetFirewallRule -DisplayName 'HTTP(S) SUP Inbound' -Profile Any -Direction Inbound -Action Allow -Protocol TCP -LocalPort @(8530,8531) -Group "For SCCM SUP" + New-NetFirewallRule -DisplayName 'HTTP(S) SUP Inbound' -Profile Any -Direction Inbound -Action Allow -Protocol TCP -LocalPort @(8530,8531) -Group "For SCCM SUP" #SUP->Internet New-NetFirewallRule -DisplayName 'HTTP Outbound' -Profile Any -Direction Outbound -Action Allow -Protocol TCP -LocalPort 80 -Group "For SCCM SUP" New-NetFirewallRule -DisplayName 'HTTP(S) Outbound' -Profile Any -Direction Outbound -Action Allow -Protocol TCP -LocalPort @(80,443) -Group "For SCCM SUP" - New-NetFirewallRule -DisplayName 'HTTP(S) Inbound' -Profile Any -Direction Inbound -Action Allow -Protocol TCP -LocalPort @(80,443) -Group "For SCCM SUP" + New-NetFirewallRule -DisplayName 'HTTP(S) Inbound' -Profile Any -Direction Inbound -Action Allow -Protocol TCP -LocalPort @(80,443) -Group "For SCCM SUP" } if($rolelist -ccontains "State Migration Point") { @@ -131,18 +131,17 @@ if($rolelist -contains "Fallback Status Point") } if($rolelist -contains "Reporting Services Point") { - New-NetFirewallRule -DisplayName 'SQL over TCP Inbound' -Profile Any -Direction Inbound -Action Allow -Protocol TCP -LocalPort 1433 -Group "For SCCM RSP" - New-NetFirewallRule -DisplayName 'SQL over TCP Outbound' -Profile Any -Direction Outbound -Action Allow -Protocol TCP -LocalPort 1433 -Group "For SCCM RSP" - New-NetFirewallRule -DisplayName 'HTTP(S) Inbound' -Profile Any -Direction Inbound -Action Allow -Protocol TCP -LocalPort @(80,443) -Group "For SCCM RSP" - New-NetFirewallRule -DisplayName 'SMB Inbound' -Profile Any -Direction Inbound -Action Allow -Protocol TCP -LocalPort 445 -Group "For SCCM RSP" - New-NetFirewallRule -DisplayName 'RPC Endpoint Mapper Inbound' -Profile Any -Direction Inbound -Action Allow -Protocol TCP -LocalPort 135 -Group "For SCCM RSP" - New-NetFirewallRule -DisplayName 'RPC Endpoint Mapper UDP Inbound' -Profile Any -Direction Inbound -Action Allow -Protocol UDP -LocalPort 135 -Group "For SCCM RSP" - #dynamic port - New-NetFirewallRule -DisplayName 'RPC Inbound' -Profile Any -Direction Inbound -Action Allow -Protocol TCP -LocalPort 1024-65535 -Group "For SCCM RSP" + New-NetFirewallRule -DisplayName 'SQL over TCP Inbound' -Profile Any -Direction Inbound -Action Allow -Protocol TCP -LocalPort 1433 -Group "For SCCM RSP" + New-NetFirewallRule -DisplayName 'SQL over TCP Outbound' -Profile Any -Direction Outbound -Action Allow -Protocol TCP -LocalPort 1433 -Group "For SCCM RSP" + New-NetFirewallRule -DisplayName 'HTTP(S) Inbound' -Profile Any -Direction Inbound -Action Allow -Protocol TCP -LocalPort @(80,443) -Group "For SCCM RSP" + New-NetFirewallRule -DisplayName 'SMB Inbound' -Profile Any -Direction Inbound -Action Allow -Protocol TCP -LocalPort 445 -Group "For SCCM RSP" + New-NetFirewallRule -DisplayName 'RPC Endpoint Mapper Inbound' -Profile Any -Direction Inbound -Action Allow -Protocol TCP -LocalPort 135 -Group "For SCCM RSP" + New-NetFirewallRule -DisplayName 'RPC Endpoint Mapper UDP Inbound' -Profile Any -Direction Inbound -Action Allow -Protocol UDP -LocalPort 135 -Group "For SCCM RSP" + #dynamic port + New-NetFirewallRule -DisplayName 'RPC Inbound' -Profile Any -Direction Inbound -Action Allow -Protocol TCP -LocalPort 1024-65535 -Group "For SCCM RSP" } if($rolelist -contains "Distribution Point") { - New-NetFirewallRule -DisplayName 'HTTP(S) Inbound' -Profile Any -Direction Inbound -Action Allow -Protocol TCP -LocalPort @(80,443) -Group "For SCCM DP" New-NetFirewallRule -DisplayName 'SMB DP Inbound' -Profile Any -Direction Inbound -Action Allow -Protocol TCP -LocalPort 445 -Group "For SCCM DP" New-NetFirewallRule -DisplayName 'Multicast Protocol Inbound' -Profile Any -Direction Inbound -Action Allow -Protocol TCP -LocalPort 63000-64000 -Group "For SCCM DP" @@ -185,9 +184,9 @@ if($rolelist -contains "Server Locator Point") if($rolelist -contains "SQL Server") { New-NetFirewallRule -DisplayName 'SQL over TCP Inbound' -Profile Any -Direction Inbound -Action Allow -Protocol TCP -LocalPort 1433 -Group "For SQL Server" - New-NetFirewallRule -DisplayName 'WMI' -Program "%systemroot%\system32\svchost.exe" -Service "winmgmt" -Profile Any -Direction Inbound -Action Allow -Protocol TCP -LocalPort any -Group "For SQL Server WMI" - New-NetFirewallRule -DisplayName 'DCOM' -Program "%systemroot%\system32\svchost.exe" -Service "rpcss" -Profile Any -Direction Inbound -Action Allow -Protocol TCP -LocalPort 135 -Group "For SQL Server DCOM" - New-NetFirewallRule -DisplayName 'SMB Provider Inbound' -Profile Any -Direction Inbound -Action Allow -Protocol TCP -LocalPort 445 -Group "For SQL Server" + New-NetFirewallRule -DisplayName 'WMI' -Program "%systemroot%\system32\svchost.exe" -Service "winmgmt" -Profile Any -Direction Inbound -Action Allow -Protocol TCP -LocalPort any -Group "For SQL Server WMI" + New-NetFirewallRule -DisplayName 'DCOM' -Program "%systemroot%\system32\svchost.exe" -Service "rpcss" -Profile Any -Direction Inbound -Action Allow -Protocol TCP -LocalPort 135 -Group "For SQL Server DCOM" + New-NetFirewallRule -DisplayName 'SMB Provider Inbound' -Profile Any -Direction Inbound -Action Allow -Protocol TCP -LocalPort 445 -Group "For SQL Server" } if($rolelist -contains "Provider") { @@ -208,12 +207,12 @@ if($rolelist -contains "Asset Intelligence Synchronization Point") } if($rolelist -contains "CM Console") { - New-NetFirewallRule -DisplayName 'RPC Outbound' -Profile Any -Direction Outbound -Action Allow -Protocol TCP -LocalPort 135 -Group "For SCCM Console" - #cm console->client - New-NetFirewallRule -DisplayName 'Remote Control(control) Outbound' -Profile Any -Direction Outbound -Action Allow -Protocol TCP -LocalPort 2701 -Group "For SCCM Console" - New-NetFirewallRule -DisplayName 'Remote Control(control) Outbound' -Profile Any -Direction Outbound -Action Allow -Protocol UDP -LocalPort 2701 -Group "For SCCM Console" - New-NetFirewallRule -DisplayName 'Remote Control(data) Outbound' -Profile Any -Direction Outbound -Action Allow -Protocol TCP -LocalPort 2702 -Group "For SCCM Console" - New-NetFirewallRule -DisplayName 'Remote Control(data) Outbound' -Profile Any -Direction Outbound -Action Allow -Protocol UDP -LocalPort 2702 -Group "For SCCM Console" - New-NetFirewallRule -DisplayName 'Remote Control(RPC Endpoint Mapper) Outbound' -Profile Any -Direction Outbound -Action Allow -Protocol TCP -LocalPort 135 -Group "For SCCM Console" - New-NetFirewallRule -DisplayName 'Remote Assistance(RDP AND RTC) Outbound' -Profile Any -Direction Outbound -Action Allow -Protocol TCP -LocalPort 3389 -Group "For SCCM Console" + New-NetFirewallRule -DisplayName 'RPC Outbound' -Profile Any -Direction Outbound -Action Allow -Protocol TCP -LocalPort 135 -Group "For SCCM Console" + #cm console->client + New-NetFirewallRule -DisplayName 'Remote Control(control) Outbound' -Profile Any -Direction Outbound -Action Allow -Protocol TCP -LocalPort 2701 -Group "For SCCM Console" + New-NetFirewallRule -DisplayName 'Remote Control(control) Outbound' -Profile Any -Direction Outbound -Action Allow -Protocol UDP -LocalPort 2701 -Group "For SCCM Console" + New-NetFirewallRule -DisplayName 'Remote Control(data) Outbound' -Profile Any -Direction Outbound -Action Allow -Protocol TCP -LocalPort 2702 -Group "For SCCM Console" + New-NetFirewallRule -DisplayName 'Remote Control(data) Outbound' -Profile Any -Direction Outbound -Action Allow -Protocol UDP -LocalPort 2702 -Group "For SCCM Console" + New-NetFirewallRule -DisplayName 'Remote Control(RPC Endpoint Mapper) Outbound' -Profile Any -Direction Outbound -Action Allow -Protocol TCP -LocalPort 135 -Group "For SCCM Console" + New-NetFirewallRule -DisplayName 'Remote Assistance(RDP AND RTC) Outbound' -Profile Any -Direction Outbound -Action Allow -Protocol TCP -LocalPort 3389 -Group "For SCCM Console" } \ No newline at end of file diff --git a/sccm-technicalpreview/scripts/SetAutoLogOn.ps1 b/sccm-technicalpreview/scripts/SetAutoLogOn.ps1 index e8a11ab6b394..94ed5b807b30 100644 --- a/sccm-technicalpreview/scripts/SetAutoLogOn.ps1 +++ b/sccm-technicalpreview/scripts/SetAutoLogOn.ps1 @@ -10,7 +10,7 @@ $isdomain = $false $NetBIOSName = "" if($DomainFullName) { - $isdomain = $true + $isdomain = $true $NetBIOSName = $DomainFullName.split('.')[0] $Username = $NetBIOSName + '\' + $Username } @@ -19,35 +19,35 @@ $RegPath = "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" "[$(Get-Date -format HH:mm:ss)] Start setting auto logon for $Username." | Out-File -Append $logpath while((Get-ItemProperty $RegPath).AutoAdminLogon -ne 1) { - "[$(Get-Date -format HH:mm:ss)] Setting AutoAdminLogon to 1." | Out-File -Append $logpath - Set-ItemProperty $RegPath "AutoAdminLogon" -Value "1" -type String + "[$(Get-Date -format HH:mm:ss)] Setting AutoAdminLogon to 1." | Out-File -Append $logpath + Set-ItemProperty $RegPath "AutoAdminLogon" -Value "1" -type String } while((Get-ItemProperty $RegPath).DefaultUsername -ne $Username) { - "[$(Get-Date -format HH:mm:ss)] Setting DefaultUsername to $Username." | Out-File -Append $logpath - Set-ItemProperty $RegPath "DefaultUsername" -Value "$Username" -type String + "[$(Get-Date -format HH:mm:ss)] Setting DefaultUsername to $Username." | Out-File -Append $logpath + Set-ItemProperty $RegPath "DefaultUsername" -Value "$Username" -type String } while((Get-ItemProperty $RegPath).DefaultPassword -ne $password) { - "[$(Get-Date -format HH:mm:ss)] Setting Password..." | Out-File -Append $logpath - Set-ItemProperty $RegPath "DefaultPassword" -Value "$password" -type String + "[$(Get-Date -format HH:mm:ss)] Setting Password..." | Out-File -Append $logpath + Set-ItemProperty $RegPath "DefaultPassword" -Value "$password" -type String } while((Get-ItemProperty $RegPath).AutoLogonCount -ne 1) { - "[$(Get-Date -format HH:mm:ss)] Setting Logon count to 1." | Out-File -Append $logpath - Set-ItemProperty $RegPath "AutoLogonCount" -Value 1 -type DWord + "[$(Get-Date -format HH:mm:ss)] Setting Logon count to 1." | Out-File -Append $logpath + Set-ItemProperty $RegPath "AutoLogonCount" -Value 1 -type DWord } if($isdomain) { - while((Get-ItemProperty $RegPath).DefaultDomainName -ne $NetBIOSName) - { - "[$(Get-Date -format HH:mm:ss)] Setting DefaultDomainName to $NetBIOSName." | Out-File -Append $logpath - Set-ItemProperty $RegPath "DefaultDomainName" -Value $NetBIOSName -type String - } + while((Get-ItemProperty $RegPath).DefaultDomainName -ne $NetBIOSName) + { + "[$(Get-Date -format HH:mm:ss)] Setting DefaultDomainName to $NetBIOSName." | Out-File -Append $logpath + Set-ItemProperty $RegPath "DefaultDomainName" -Value $NetBIOSName -type String + } } "[$(Get-Date -format HH:mm:ss)] Finished." | Out-File -Append $logpath \ No newline at end of file diff --git a/sccm-technicalpreview/scripts/UpgradeSCCM.ps1 b/sccm-technicalpreview/scripts/UpgradeSCCM.ps1 index da3c63412d59..672446d91177 100644 --- a/sccm-technicalpreview/scripts/UpgradeSCCM.ps1 +++ b/sccm-technicalpreview/scripts/UpgradeSCCM.ps1 @@ -7,19 +7,19 @@ if(!(Test-Path $ProvisionToolPath)) } $logpath = $ProvisionToolPath+"\UpgradeCMlog.txt" $SiteCode = Get-ItemPropertyValue -Path 'HKLM:\SOFTWARE\Microsoft\SMS\Identification' -Name 'Site Code' - + $ProviderMachineName = $env:COMPUTERNAME+"."+$DomainFullName # SMS Provider machine name # Customizations $initParams = @{} if($ENV:SMS_ADMIN_UI_PATH -eq $null) { - $ENV:SMS_ADMIN_UI_PATH = "C:\Program Files (x86)\Microsoft Configuration Manager\AdminConsole\bin\i386" + $ENV:SMS_ADMIN_UI_PATH = "C:\Program Files (x86)\Microsoft Configuration Manager\AdminConsole\bin\i386" } # Import the ConfigurationManager.psd1 module if((Get-Module ConfigurationManager) -eq $null) { - Import-Module "$($ENV:SMS_ADMIN_UI_PATH)\..\ConfigurationManager.psd1" @initParams + Import-Module "$($ENV:SMS_ADMIN_UI_PATH)\..\ConfigurationManager.psd1" @initParams } # Connect to the site's drive if it is not already present @@ -28,9 +28,9 @@ New-PSDrive -Name $SiteCode -PSProvider CMSite -Root $ProviderMachineName @initP while((Get-PSDrive -Name $SiteCode -PSProvider CMSite -ErrorAction SilentlyContinue) -eq $null) { - "[$(Get-Date -format HH:mm:ss)] Retry in 10s to set PS Drive. Please wait." | Out-File -Append $logpath - Start-Sleep -Seconds 10 - New-PSDrive -Name $SiteCode -PSProvider CMSite -Root $ProviderMachineName @initParams + "[$(Get-Date -format HH:mm:ss)] Retry in 10s to set PS Drive. Please wait." | Out-File -Append $logpath + Start-Sleep -Seconds 10 + New-PSDrive -Name $SiteCode -PSProvider CMSite -Root $ProviderMachineName @initParams } # Set the current location to be the site code. @@ -158,114 +158,114 @@ while($updatepack -ne "") if($retrytimes -eq 3) { $upgradingfailed = $true - break; + break } $updatepack = Get-CMSiteUpdate -Fast -Name $updatepack.Name - while($updatepack.State -eq 327682 -or $updatepack.State -eq 262145 -or $updatepack.State -eq 327679) - { - #package not downloaded - if($updatepack.State -eq 327682) - { - Invoke-CMSiteUpdateDownload -Name $updatepack.Name -Force -WarningAction SilentlyContinue - Start-Sleep 120 - $updatepack = Get-CMSiteUpdate -Name $updatepack.Name -Fast - $downloadstarttime = get-date - while($updatepack.State -eq 327682) - { - "[$(Get-Date -format HH:mm:ss)] Waiting SCCM Upgrade package start to download, sleep 2 min..." | Out-File -Append $logpath - Start-Sleep 120 - $updatepack = Get-CMSiteUpdate -Name $updatepack.Name -Fast - $downloadspan = New-TimeSpan -Start $downloadstarttime -End (Get-Date) - if($downloadspan.Hours -ge 1) - { - Restart-Service -DisplayName "SMS_Executive" - Start-Sleep 120 - $downloadstarttime = get-date - } - } - } - #waiting package downloaded - $downloadstarttime = get-date - while($updatepack.State -eq 262145) - { - "[$(Get-Date -format HH:mm:ss)] Waiting SCCM Upgrade package download, sleep 2 min..." | Out-File -Append $logpath - Start-Sleep 120 - $updatepack = Get-CMSiteUpdate -Name $updatepack.Name -Fast - $downloadspan = New-TimeSpan -Start $downloadstarttime -End (Get-Date) - if($downloadspan.Hours -ge 1) - { - Restart-Service -DisplayName "SMS_Executive" - Start-Sleep 120 - $downloadstarttime = get-date - } - } + while($updatepack.State -eq 327682 -or $updatepack.State -eq 262145 -or $updatepack.State -eq 327679) + { + #package not downloaded + if($updatepack.State -eq 327682) + { + Invoke-CMSiteUpdateDownload -Name $updatepack.Name -Force -WarningAction SilentlyContinue + Start-Sleep 120 + $updatepack = Get-CMSiteUpdate -Name $updatepack.Name -Fast + $downloadstarttime = get-date + while($updatepack.State -eq 327682) + { + "[$(Get-Date -format HH:mm:ss)] Waiting SCCM Upgrade package start to download, sleep 2 min..." | Out-File -Append $logpath + Start-Sleep 120 + $updatepack = Get-CMSiteUpdate -Name $updatepack.Name -Fast + $downloadspan = New-TimeSpan -Start $downloadstarttime -End (Get-Date) + if($downloadspan.Hours -ge 1) + { + Restart-Service -DisplayName "SMS_Executive" + Start-Sleep 120 + $downloadstarttime = get-date + } + } + } + #waiting package downloaded + $downloadstarttime = get-date + while($updatepack.State -eq 262145) + { + "[$(Get-Date -format HH:mm:ss)] Waiting SCCM Upgrade package download, sleep 2 min..." | Out-File -Append $logpath + Start-Sleep 120 + $updatepack = Get-CMSiteUpdate -Name $updatepack.Name -Fast + $downloadspan = New-TimeSpan -Start $downloadstarttime -End (Get-Date) + if($downloadspan.Hours -ge 1) + { + Restart-Service -DisplayName "SMS_Executive" + Start-Sleep 120 + $downloadstarttime = get-date + } + } - #downloading failed - if($updatepack.State -eq 327679) - { - $retrytimes++; - Start-Sleep 300 - continue; - } - } - #trigger prerequisites check after the package downloaded - Invoke-CMSiteUpdatePrerequisiteCheck -Name $updatepack.Name - while($updatepack.State -ne 196607 -and $updatepack.State -ne 131074 -and $updatepack.State -ne 131075) - { - ("[$(Get-Date -format HH:mm:ss)] Waiting checking prerequisites complete, current pack " + $updatepack.Name + " state is " + ($state.($updatepack.State)) + ", sleep 2 min...") | Out-File -Append $logpath - Start-Sleep 120 - $updatepack = Get-CMSiteUpdate -Fast -Name $updatepack.Name - } - if($updatepack.State -eq 196607) - { - $retrytimes++; - Start-Sleep 300 - continue; - } - #trigger setup after the prerequisites check - Install-CMSiteUpdate -Name $updatepack.Name -SkipPrerequisiteCheck -Force - while($updatepack.State -ne 196607 -and $updatepack.State -ne 262143 -and $updatepack.State -ne 196612) - { - ("[$(Get-Date -format HH:mm:ss)] Waiting SCCM Upgrade Complete, current pack " + $updatepack.Name + " state is " + ($state.($updatepack.State)) + ", sleep 2 min...") | Out-File -Append $logpath - Start-Sleep 120 - $updatepack = Get-CMSiteUpdate -Fast -Name $updatepack.Name - } - if($updatepack.State -eq 196612) - { - ("[$(Get-Date -format HH:mm:ss)] SCCM Upgrade Complete, current pack " + $updatepack.Name + " state is " + ($state.($updatepack.State)) ) | Out-File -Append $logpath - #we need waiting the copying files finished if there is only one site - $toplevelsite = Get-CMSite |where {$_.ReportingSiteCode -eq ""} - if((Get-CMSite).count -eq 1) - { - $path= Get-ItemPropertyValue -Path 'HKLM:\SOFTWARE\Microsoft\SMS\Setup' -Name 'Installation Directory' + #downloading failed + if($updatepack.State -eq 327679) + { + $retrytimes++ + Start-Sleep 300 + continue + } + } + #trigger prerequisites check after the package downloaded + Invoke-CMSiteUpdatePrerequisiteCheck -Name $updatepack.Name + while($updatepack.State -ne 196607 -and $updatepack.State -ne 131074 -and $updatepack.State -ne 131075) + { + ("[$(Get-Date -format HH:mm:ss)] Waiting checking prerequisites complete, current pack " + $updatepack.Name + " state is " + ($state.($updatepack.State)) + ", sleep 2 min...") | Out-File -Append $logpath + Start-Sleep 120 + $updatepack = Get-CMSiteUpdate -Fast -Name $updatepack.Name + } + if($updatepack.State -eq 196607) + { + $retrytimes++ + Start-Sleep 300 + continue + } + #trigger setup after the prerequisites check + Install-CMSiteUpdate -Name $updatepack.Name -SkipPrerequisiteCheck -Force + while($updatepack.State -ne 196607 -and $updatepack.State -ne 262143 -and $updatepack.State -ne 196612) + { + ("[$(Get-Date -format HH:mm:ss)] Waiting SCCM Upgrade Complete, current pack " + $updatepack.Name + " state is " + ($state.($updatepack.State)) + ", sleep 2 min...") | Out-File -Append $logpath + Start-Sleep 120 + $updatepack = Get-CMSiteUpdate -Fast -Name $updatepack.Name + } + if($updatepack.State -eq 196612) + { + ("[$(Get-Date -format HH:mm:ss)] SCCM Upgrade Complete, current pack " + $updatepack.Name + " state is " + ($state.($updatepack.State)) ) | Out-File -Append $logpath + #we need waiting the copying files finished if there is only one site + $toplevelsite = Get-CMSite |where {$_.ReportingSiteCode -eq ""} + if((Get-CMSite).count -eq 1) + { + $path= Get-ItemPropertyValue -Path 'HKLM:\SOFTWARE\Microsoft\SMS\Setup' -Name 'Installation Directory' - $fileversion=(Get-Item ($path+'\cd.latest\SMSSETUP\BIN\X64\setup.exe')).VersionInfo.FileVersion.split('.')[2] - while($fileversion -ne $toplevelsite.BuildNumber) - { - Start-Sleep 120 - $fileversion=(Get-Item ($path+'\cd.latest\SMSSETUP\BIN\X64\setup.exe')).VersionInfo.FileVersion.split('.')[2] - } - #Wait for copying files finished - Start-Sleep 600 - } - #Get if there are any other updates need to be installed - $updatepack = getupdate - } - if($updatepack.State -eq 196607 -or $updatepack.State -eq 262143 ) - { - if($retrytimes -le 3) - { - $upgradingfailed = $true - Start-Sleep 300 - continue; - } - $retrytimes = $retrytimes + 1; - } + $fileversion=(Get-Item ($path+'\cd.latest\SMSSETUP\BIN\X64\setup.exe')).VersionInfo.FileVersion.split('.')[2] + while($fileversion -ne $toplevelsite.BuildNumber) + { + Start-Sleep 120 + $fileversion=(Get-Item ($path+'\cd.latest\SMSSETUP\BIN\X64\setup.exe')).VersionInfo.FileVersion.split('.')[2] + } + #Wait for copying files finished + Start-Sleep 600 + } + #Get if there are any other updates need to be installed + $updatepack = getupdate + } + if($updatepack.State -eq 196607 -or $updatepack.State -eq 262143 ) + { + if($retrytimes -le 3) + { + $upgradingfailed = $true + Start-Sleep 300 + continue + } + $retrytimes = $retrytimes + 1 + } } if($upgradingfailed -eq $true) { - ("[$(Get-Date -format HH:mm:ss)] Upgrade " + $updatepack.Name + " failed") | Out-File -Append $logpath - throw + ("[$(Get-Date -format HH:mm:ss)] Upgrade " + $updatepack.Name + " failed") | Out-File -Append $logpath + throw } \ No newline at end of file diff --git a/sccm-technicalpreview/scripts/WorkFlow-DC.ps1 b/sccm-technicalpreview/scripts/WorkFlow-DC.ps1 index 4faf3236dda5..05ae934ee803 100644 --- a/sccm-technicalpreview/scripts/WorkFlow-DC.ps1 +++ b/sccm-technicalpreview/scripts/WorkFlow-DC.ps1 @@ -24,71 +24,74 @@ $AzcopyPath = "C:\Program Files (x86)\Microsoft SDKs\Azure\AzCopy" if(!(Test-Path $AzcopyPath)) { - $path = "$ProvisionToolPath\azcopy.msi" - if(!(Test-Path $path)) - { - #Download azcopy - $url = "http://aka.ms/downloadazcopy" - Invoke-WebRequest -Uri $url -OutFile $path - } + $path = "$ProvisionToolPath\azcopy.msi" + if(!(Test-Path $path)) + { + #Download azcopy + $url = "http://aka.ms/downloadazcopy" + Invoke-WebRequest -Uri $url -OutFile $path + } - #Install azcopy - Start-Process msiexec.exe -Wait -ArgumentList "/I $path /quiet" + #Install azcopy + Start-Process msiexec.exe -Wait -ArgumentList "/I $path /quiet" } $sourceDirctory = (split-path -parent $MyInvocation.MyCommand.Definition) + "\*" $destDirctory = "$ProvisionToolPath\" Copy-item -Force -Recurse $sourceDirctory -Destination $destDirctory -$ConfigurationFile = Join-Path -Path $ProvisionToolPath -ChildPath "$Role.json"; +$ConfigurationFile = Join-Path -Path $ProvisionToolPath -ChildPath "$Role.json" -if (Test-Path -Path $ConfigurationFile) { - $Configuration = Get-Content -Path $ConfigurationFile | ConvertFrom-Json; -} else { +if (Test-Path -Path $ConfigurationFile) +{ + $Configuration = Get-Content -Path $ConfigurationFile | ConvertFrom-Json +} +else +{ [hashtable]$Actions = @{ - Name = $env:COMPUTERNAME - InstallADDS = @{ + Name = $env:COMPUTERNAME + InstallADDS = @{ Status = 'NotStart' StartTime = '' EndTime = '' } - SetAutoLogOn = @{ + SetAutoLogOn = @{ Status = 'NotStart' StartTime = '' EndTime = '' } - TurnOnFirewallPort= @{ + TurnOnFirewallPort = @{ Status = 'NotStart' StartTime = '' EndTime = '' } - InstallRolesAndFeatures= @{ + InstallRolesAndFeatures = @{ Status = 'NotStart' StartTime = '' EndTime = '' } - WaitForPS= @{ + WaitForPS = @{ Status = 'NotStart' StartTime = '' EndTime = '' } - DelegateControl= @{ + DelegateControl = @{ Status = 'NotStart' StartTime = '' EndTime = '' } - ExendADSchema= @{ + ExendADSchema = @{ Status = 'NotStart' StartTime = '' EndTime = '' } - CleanUp= @{ + CleanUp = @{ Status = 'NotStart' StartTime = '' EndTime = '' } - }; - $Configuration = New-Object -TypeName psobject -Property $Actions; + } + $Configuration = New-Object -TypeName psobject -Property $Actions } $Configuration | Add-Member -MemberType ScriptMethod -Name SetRebootConfig -Value { @@ -101,24 +104,24 @@ $Configuration | Add-Member -MemberType ScriptMethod -Name SetRebootConfig -Valu $Command | Out-File -FilePath $BatchFilePath -Encoding ascii -Append $RunOnceRegKey = 'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce' - $KeyValueName = 'Workflow Reboot'; + $KeyValueName = 'Workflow Reboot' $KeyType = [Microsoft.Win32.RegistryValueKind]::String $null = [Microsoft.Win32.Registry]::SetValue($RunOnceRegKey,$KeyValueName,$BatchFile,$KeyType) $this | ConvertTo-Json | Out-File -FilePath $ConfigurationFile -Force $configfile = $ConfigurationFile - $uploadurl = $tempurl + "/$Role.json" - AZCopy -source $configfile -dest $uploadurl -upload $true + $uploadurl = $tempurl + "/$Role.json" + AZCopy -source $configfile -dest $uploadurl -upload $true return 0 } catch { return 1 - }; + } } $Mainscript = $ProvisionToolPath + "\main.ps1" -. $Mainscript; +. $Mainscript if ($Configuration.InstallADDS.Status -eq 'NotStart') { $Configuration.InstallADDS.Status = 'Running' @@ -153,7 +156,7 @@ if ($Configuration.InstallADDS.Status -eq 'Completed') { $Result = $Configuration.SetRebootConfig() if ($Result -eq 0) { shutdown -r -t 10 - exit 0 + exit 0 } } else @@ -188,7 +191,7 @@ if ($Configuration.InstallADDS.Status -eq 'Completed') { $Configuration.InstallRolesAndFeatures.Status = 'Completed' $Configuration.InstallRolesAndFeatures.EndTime = Get-Date -format "yyyy-MM-dd HH:mm:ss" } - else + else { $Configuration.InstallRolesAndFeatures.Status = 'Error' $Configuration.InstallRolesAndFeatures.EndTime = Get-Date -format "yyyy-MM-dd HH:mm:ss" @@ -217,7 +220,7 @@ if ($Configuration.InstallADDS.Status -eq 'Completed') { $Configuration.DelegateControl.Status = 'Running' $Configuration.DelegateControl.StartTime = Get-Date -format "yyyy-MM-dd HH:mm:ss" UploadConfigFile - [string[]]$AddPermissionRoleList = "PS1","DP_MP" + [string[]]$AddPermissionRoleList = "PS1","DP_MP" $Result = Delegate-Control $DomainFullName $AddPermissionRoleList if ($Result[-1] -eq 0) { $Configuration.DelegateControl.Status = 'Completed' @@ -231,7 +234,7 @@ if ($Configuration.InstallADDS.Status -eq 'Completed') { UploadConfigFile } - if ($Configuration.ExendADSchema.Status -eq 'NotStart') { + if ($Configuration.ExendADSchema.Status -eq 'NotStart') { $Configuration.ExendADSchema.Status = 'Running' $Configuration.ExendADSchema.StartTime = Get-Date -format "yyyy-MM-dd HH:mm:ss" UploadConfigFile @@ -248,7 +251,7 @@ if ($Configuration.InstallADDS.Status -eq 'Completed') { UploadConfigFile } - if ($Configuration.CleanUp.Status -eq 'NotStart') { + if ($Configuration.CleanUp.Status -eq 'NotStart') { $Configuration.CleanUp.Status = 'Running' $Configuration.CleanUp.StartTime = Get-Date -format "yyyy-MM-dd HH:mm:ss" UploadConfigFile @@ -257,7 +260,7 @@ if ($Configuration.InstallADDS.Status -eq 'Completed') { $Configuration.CleanUp.Status = 'Completed' $Configuration.CleanUp.EndTime = Get-Date -format "yyyy-MM-dd HH:mm:ss" } - UploadConfigFile + UploadConfigFile } } diff --git a/sccm-technicalpreview/scripts/WorkFlow-DPMP.ps1 b/sccm-technicalpreview/scripts/WorkFlow-DPMP.ps1 index 710261d27d14..18e90a6f694f 100644 --- a/sccm-technicalpreview/scripts/WorkFlow-DPMP.ps1 +++ b/sccm-technicalpreview/scripts/WorkFlow-DPMP.ps1 @@ -25,71 +25,74 @@ $AzcopyPath = "C:\Program Files (x86)\Microsoft SDKs\Azure\AzCopy" if(!(Test-Path $AzcopyPath)) { - $path = "$ProvisionToolPath\azcopy.msi" - if(!(Test-Path $path)) - { - #Download azcopy - $url = "http://aka.ms/downloadazcopy" - Invoke-WebRequest -Uri $url -OutFile $path - } - - #Install azcopy - Start-Process msiexec.exe -Wait -ArgumentList "/I $path /quiet" + $path = "$ProvisionToolPath\azcopy.msi" + if(!(Test-Path $path)) + { + #Download azcopy + $url = "http://aka.ms/downloadazcopy" + Invoke-WebRequest -Uri $url -OutFile $path + } + + #Install azcopy + Start-Process msiexec.exe -Wait -ArgumentList "/I $path /quiet" } $sourceDirctory = (split-path -parent $MyInvocation.MyCommand.Definition) + "\*" $destDirctory = "$ProvisionToolPath\" Copy-item -Force -Recurse $sourceDirctory -Destination $destDirctory -$ConfigurationFile = Join-Path -Path $ProvisionToolPath -ChildPath "$Role.json"; +$ConfigurationFile = Join-Path -Path $ProvisionToolPath -ChildPath "$Role.json" -if (Test-Path -Path $ConfigurationFile) { - $Configuration = Get-Content -Path $ConfigurationFile | ConvertFrom-Json; -} else { +if (Test-Path -Path $ConfigurationFile) +{ + $Configuration = Get-Content -Path $ConfigurationFile | ConvertFrom-Json +} +else +{ [hashtable]$Actions = @{ - Name = $env:COMPUTERNAME - WaitForDC = @{ + Name = $env:COMPUTERNAME + WaitForDC = @{ Status = 'NotStart' StartTime = '' EndTime = '' } - JoinDomain = @{ + JoinDomain = @{ Status = 'NotStart' StartTime = '' EndTime = '' } - SetAutoLogOn = @{ + SetAutoLogOn = @{ Status = 'NotStart' StartTime = '' EndTime = '' } - TurnOnFirewallPort= @{ + TurnOnFirewallPort = @{ Status = 'NotStart' StartTime = '' EndTime = '' } - InstallRolesAndFeatures= @{ + InstallRolesAndFeatures = @{ Status = 'NotStart' StartTime = '' EndTime = '' } - WaitForPS= @{ + WaitForPS = @{ Status = 'NotStart' StartTime = '' EndTime = '' } - AddPermission= @{ + AddPermission = @{ Status = 'NotStart' StartTime = '' EndTime = '' } - CleanUp= @{ + CleanUp = @{ Status = 'NotStart' StartTime = '' EndTime = '' } - }; - $Configuration = New-Object -TypeName psobject -Property $Actions; + } + $Configuration = New-Object -TypeName psobject -Property $Actions } $Configuration | Add-Member -MemberType ScriptMethod -Name SetRebootConfig -Value { @@ -102,24 +105,24 @@ $Configuration | Add-Member -MemberType ScriptMethod -Name SetRebootConfig -Valu $Command | Out-File -FilePath $BatchFilePath -Encoding ascii -Append $RunOnceRegKey = 'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce' - $KeyValueName = 'Workflow Reboot'; - $KeyType = [Microsoft.Win32.RegistryValueKind]::String; + $KeyValueName = 'Workflow Reboot' + $KeyType = [Microsoft.Win32.RegistryValueKind]::String $null = [Microsoft.Win32.Registry]::SetValue($RunOnceRegKey,$KeyValueName,$BatchFile,$KeyType) $this | ConvertTo-Json | Out-File -FilePath $ConfigurationFile -Force $configfile = $ConfigurationFile - $uploadurl = $tempurl + "/$Role.json" - AZCopy -source $configfile -dest $uploadurl -upload $true + $uploadurl = $tempurl + "/$Role.json" + AZCopy -source $configfile -dest $uploadurl -upload $true return 0 } catch { return 1 - }; + } } $Mainscript = $ProvisionToolPath + "\main.ps1" -. $Mainscript; +. $Mainscript if ($Configuration.WaitForDC.Status -eq 'NotStart') { $Configuration.WaitForDC.Status = 'Running' @@ -172,7 +175,7 @@ if($Configuration.WaitForDC.Status -eq 'Completed') $Result = $Configuration.SetRebootConfig() if ($Result -eq 0) { shutdown -r -t 10 - exit 0 + exit 0 } } else @@ -186,17 +189,17 @@ if($Configuration.WaitForDC.Status -eq 'Completed') Enable-RDP - if ($Configuration.TurnOnFirewallPort.Status -eq 'NotStart') { - $Configuration.TurnOnFirewallPort.Status = 'Running' - $Configuration.TurnOnFirewallPort.StartTime = Get-Date -format "yyyy-MM-dd HH:mm:ss" - UploadConfigFile - $Result = TurnOn-FirewallPort $rolelist - if ($Result[-1] -eq 0) { - $Configuration.TurnOnFirewallPort.Status = 'Completed' - $Configuration.TurnOnFirewallPort.EndTime = Get-Date -format "yyyy-MM-dd HH:mm:ss" - } - UploadConfigFile - } + if ($Configuration.TurnOnFirewallPort.Status -eq 'NotStart') { + $Configuration.TurnOnFirewallPort.Status = 'Running' + $Configuration.TurnOnFirewallPort.StartTime = Get-Date -format "yyyy-MM-dd HH:mm:ss" + UploadConfigFile + $Result = TurnOn-FirewallPort $rolelist + if ($Result[-1] -eq 0) { + $Configuration.TurnOnFirewallPort.Status = 'Completed' + $Configuration.TurnOnFirewallPort.EndTime = Get-Date -format "yyyy-MM-dd HH:mm:ss" + } + UploadConfigFile + } if ($Configuration.InstallRolesAndFeatures.Status -eq 'NotStart') { $Configuration.InstallRolesAndFeatures.Status = 'Running' @@ -219,10 +222,10 @@ if($Configuration.WaitForDC.Status -eq 'Completed') $Configuration.WaitForPS.Status = 'Completed' $Configuration.WaitForPS.EndTime = Get-Date -format "yyyy-MM-dd HH:mm:ss" } - UploadConfigFile + UploadConfigFile } - if ($Configuration.AddPermission.Status -eq 'NotStart') { + if ($Configuration.AddPermission.Status -eq 'NotStart') { $Configuration.AddPermission.Status = 'Running' $Configuration.AddPermission.StartTime = Get-Date -format "yyyy-MM-dd HH:mm:ss" UploadConfigFile @@ -231,20 +234,20 @@ if($Configuration.WaitForDC.Status -eq 'Completed') $Configuration.AddPermission.Status = 'Completed' $Configuration.AddPermission.EndTime = Get-Date -format "yyyy-MM-dd HH:mm:ss" } - UploadConfigFile - } - - if ($Configuration.CleanUp.Status -eq 'NotStart') { - $Configuration.CleanUp.Status = 'Running' - $Configuration.CleanUp.StartTime = Get-Date -format "yyyy-MM-dd HH:mm:ss" - UploadConfigFile - $Result = Clean-Up - if ($Result[-1] -eq 0) { - $Configuration.CleanUp.Status = 'Completed' - $Configuration.CleanUp.EndTime = Get-Date -format "yyyy-MM-dd HH:mm:ss" - } - UploadConfigFile - } + UploadConfigFile + } + + if ($Configuration.CleanUp.Status -eq 'NotStart') { + $Configuration.CleanUp.Status = 'Running' + $Configuration.CleanUp.StartTime = Get-Date -format "yyyy-MM-dd HH:mm:ss" + UploadConfigFile + $Result = Clean-Up + if ($Result[-1] -eq 0) { + $Configuration.CleanUp.Status = 'Completed' + $Configuration.CleanUp.EndTime = Get-Date -format "yyyy-MM-dd HH:mm:ss" + } + UploadConfigFile + } } } diff --git a/sccm-technicalpreview/scripts/WorkFlow-Other.ps1 b/sccm-technicalpreview/scripts/WorkFlow-Other.ps1 index de23f2bcc295..5d70b237ad3d 100644 --- a/sccm-technicalpreview/scripts/WorkFlow-Other.ps1 +++ b/sccm-technicalpreview/scripts/WorkFlow-Other.ps1 @@ -24,71 +24,74 @@ $AzcopyPath = "C:\Program Files (x86)\Microsoft SDKs\Azure\AzCopy" if(!(Test-Path $AzcopyPath)) { - $path = "$ProvisionToolPath\azcopy.msi" - if(!(Test-Path $path)) - { - #Download azcopy - $url = "http://aka.ms/downloadazcopy" - Invoke-WebRequest -Uri $url -OutFile $path - } - - #Install azcopy - Start-Process msiexec.exe -Wait -ArgumentList "/I $path /quiet" + $path = "$ProvisionToolPath\azcopy.msi" + if(!(Test-Path $path)) + { + #Download azcopy + $url = "http://aka.ms/downloadazcopy" + Invoke-WebRequest -Uri $url -OutFile $path + } + + #Install azcopy + Start-Process msiexec.exe -Wait -ArgumentList "/I $path /quiet" } $sourceDirctory = (split-path -parent $MyInvocation.MyCommand.Definition) + "\*" $destDirctory = "$ProvisionToolPath\" Copy-item -Force -Recurse $sourceDirctory -Destination $destDirctory -$ConfigurationFile = Join-Path -Path $ProvisionToolPath -ChildPath "$Role.json"; +$ConfigurationFile = Join-Path -Path $ProvisionToolPath -ChildPath "$Role.json" -if (Test-Path -Path $ConfigurationFile) { - $Configuration = Get-Content -Path $ConfigurationFile | ConvertFrom-Json; -} else { +if (Test-Path -Path $ConfigurationFile) +{ + $Configuration = Get-Content -Path $ConfigurationFile | ConvertFrom-Json +} +else +{ [hashtable]$Actions = @{ - Name = $env:COMPUTERNAME - WaitForDC = @{ + Name = $env:COMPUTERNAME + WaitForDC = @{ Status = 'NotStart' StartTime = '' EndTime = '' } - JoinDomain = @{ + JoinDomain = @{ Status = 'NotStart' StartTime = '' EndTime = '' } - SetAutoLogOn = @{ + SetAutoLogOn = @{ Status = 'NotStart' StartTime = '' EndTime = '' } - TurnOnFirewallPort= @{ + TurnOnFirewallPort = @{ Status = 'NotStart' StartTime = '' EndTime = '' } - InstallRolesAndFeatures= @{ + InstallRolesAndFeatures = @{ Status = 'NotStart' StartTime = '' EndTime = '' } - WaitForPS= @{ + WaitForPS = @{ Status = 'NotStart' StartTime = '' EndTime = '' } - AddPermission= @{ + AddPermission = @{ Status = 'NotStart' StartTime = '' EndTime = '' } - CleanUp= @{ + CleanUp = @{ Status = 'NotStart' StartTime = '' EndTime = '' } - }; - $Configuration = New-Object -TypeName psobject -Property $Actions; + } + $Configuration = New-Object -TypeName psobject -Property $Actions } $Configuration | Add-Member -MemberType ScriptMethod -Name SetRebootConfig -Value { @@ -101,24 +104,24 @@ $Configuration | Add-Member -MemberType ScriptMethod -Name SetRebootConfig -Valu $Command | Out-File -FilePath $BatchFilePath -Encoding ascii -Append $RunOnceRegKey = 'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce' - $KeyValueName = 'Workflow Reboot'; - $KeyType = [Microsoft.Win32.RegistryValueKind]::String; + $KeyValueName = 'Workflow Reboot' + $KeyType = [Microsoft.Win32.RegistryValueKind]::String $null = [Microsoft.Win32.Registry]::SetValue($RunOnceRegKey,$KeyValueName,$BatchFile,$KeyType) $this | ConvertTo-Json | Out-File -FilePath $ConfigurationFile -Force $configfile = $ConfigurationFile - $uploadurl = $tempurl + "/$Role.json" - AZCopy -source $configfile -dest $uploadurl -upload $true + $uploadurl = $tempurl + "/$Role.json" + AZCopy -source $configfile -dest $uploadurl -upload $true return 0 } catch { return 1 - }; + } } $Mainscript = $ProvisionToolPath + "\main.ps1" -. $Mainscript; +. $Mainscript if ($Configuration.WaitForDC.Status -eq 'NotStart') { $Configuration.WaitForDC.Status = 'Running' @@ -171,7 +174,7 @@ if($Configuration.WaitForDC.Status -eq 'Completed') $Result = $Configuration.SetRebootConfig() if ($Result -eq 0) { shutdown -r -t 10 - exit 0 + exit 0 } } else @@ -185,17 +188,17 @@ if($Configuration.WaitForDC.Status -eq 'Completed') Enable-RDP - if ($Configuration.TurnOnFirewallPort.Status -eq 'NotStart') { - $Configuration.TurnOnFirewallPort.Status = 'Running' - $Configuration.TurnOnFirewallPort.StartTime = Get-Date -format "yyyy-MM-dd HH:mm:ss" - UploadConfigFile - $Result = TurnOn-FirewallPort - if ($Result[-1] -eq 0) { - $Configuration.TurnOnFirewallPort.Status = 'Completed' - $Configuration.TurnOnFirewallPort.EndTime = Get-Date -format "yyyy-MM-dd HH:mm:ss" - } - UploadConfigFile - } + if ($Configuration.TurnOnFirewallPort.Status -eq 'NotStart') { + $Configuration.TurnOnFirewallPort.Status = 'Running' + $Configuration.TurnOnFirewallPort.StartTime = Get-Date -format "yyyy-MM-dd HH:mm:ss" + UploadConfigFile + $Result = TurnOn-FirewallPort + if ($Result[-1] -eq 0) { + $Configuration.TurnOnFirewallPort.Status = 'Completed' + $Configuration.TurnOnFirewallPort.EndTime = Get-Date -format "yyyy-MM-dd HH:mm:ss" + } + UploadConfigFile + } if ($Configuration.InstallRolesAndFeatures.Status -eq 'NotStart') { $Configuration.InstallRolesAndFeatures.Status = 'Running' @@ -209,7 +212,7 @@ if($Configuration.WaitForDC.Status -eq 'Completed') UploadConfigFile } - if ($Configuration.WaitForPS.Status -eq 'NotStart') { + if ($Configuration.WaitForPS.Status -eq 'NotStart') { $Configuration.WaitForPS.Status = 'Running' $Configuration.WaitForPS.StartTime = Get-Date -format "yyyy-MM-dd HH:mm:ss" UploadConfigFile @@ -218,10 +221,10 @@ if($Configuration.WaitForDC.Status -eq 'Completed') $Configuration.WaitForPS.Status = 'Completed' $Configuration.WaitForPS.EndTime = Get-Date -format "yyyy-MM-dd HH:mm:ss" } - UploadConfigFile + UploadConfigFile } - if ($Configuration.AddPermission.Status -eq 'NotStart') { + if ($Configuration.AddPermission.Status -eq 'NotStart') { $Configuration.AddPermission.Status = 'Running' $Configuration.AddPermission.StartTime = Get-Date -format "yyyy-MM-dd HH:mm:ss" UploadConfigFile @@ -230,20 +233,20 @@ if($Configuration.WaitForDC.Status -eq 'Completed') $Configuration.AddPermission.Status = 'Completed' $Configuration.AddPermission.EndTime = Get-Date -format "yyyy-MM-dd HH:mm:ss" } - UploadConfigFile - } - - if ($Configuration.CleanUp.Status -eq 'NotStart') { - $Configuration.CleanUp.Status = 'Running' - $Configuration.CleanUp.StartTime = Get-Date -format "yyyy-MM-dd HH:mm:ss" - UploadConfigFile - $Result = Clean-Up - if ($Result[-1] -eq 0) { - $Configuration.CleanUp.Status = 'Completed' - $Configuration.CleanUp.EndTime = Get-Date -format "yyyy-MM-dd HH:mm:ss" - } - UploadConfigFile - } + UploadConfigFile + } + + if ($Configuration.CleanUp.Status -eq 'NotStart') { + $Configuration.CleanUp.Status = 'Running' + $Configuration.CleanUp.StartTime = Get-Date -format "yyyy-MM-dd HH:mm:ss" + UploadConfigFile + $Result = Clean-Up + if ($Result[-1] -eq 0) { + $Configuration.CleanUp.Status = 'Completed' + $Configuration.CleanUp.EndTime = Get-Date -format "yyyy-MM-dd HH:mm:ss" + } + UploadConfigFile + } } } diff --git a/sccm-technicalpreview/scripts/WorkFlow-PS1.ps1 b/sccm-technicalpreview/scripts/WorkFlow-PS1.ps1 index cdd2a5316152..4743d718f2b2 100644 --- a/sccm-technicalpreview/scripts/WorkFlow-PS1.ps1 +++ b/sccm-technicalpreview/scripts/WorkFlow-PS1.ps1 @@ -25,33 +25,36 @@ $AzcopyPath = "C:\Program Files (x86)\Microsoft SDKs\Azure\AzCopy" if(!(Test-Path $AzcopyPath)) { - $path = "$ProvisionToolPath\azcopy.msi" - if(!(Test-Path $path)) - { - #Download azcopy - $url = "http://aka.ms/downloadazcopy" - Invoke-WebRequest -Uri $url -OutFile $path - } - - #Install azcopy - Start-Process msiexec.exe -Wait -ArgumentList "/I $path /quiet" + $path = "$ProvisionToolPath\azcopy.msi" + if(!(Test-Path $path)) + { + #Download azcopy + $url = "http://aka.ms/downloadazcopy" + Invoke-WebRequest -Uri $url -OutFile $path + } + + #Install azcopy + Start-Process msiexec.exe -Wait -ArgumentList "/I $path /quiet" } $sourceDirctory = (split-path -parent $MyInvocation.MyCommand.Definition) + "\*" $destDirctory = "$ProvisionToolPath\" Copy-item -Force -Recurse $sourceDirctory -Destination $destDirctory -$ConfigurationFile = Join-Path -Path $ProvisionToolPath -ChildPath "$Role.json"; +$ConfigurationFile = Join-Path -Path $ProvisionToolPath -ChildPath "$Role.json" -if (Test-Path -Path $ConfigurationFile) { - $Configuration = Get-Content -Path $ConfigurationFile | ConvertFrom-Json; -} else { +if (Test-Path -Path $ConfigurationFile) +{ + $Configuration = Get-Content -Path $ConfigurationFile | ConvertFrom-Json +} +else +{ [hashtable]$Actions = @{ - Name = $env:COMPUTERNAME - SQLInstanceName = "" - SQLDataFilePath = "" - SQLLogFilePath = "" - AddBuiltinPermission = @{ + Name = $env:COMPUTERNAME + SQLInstanceName = "" + SQLDataFilePath = "" + SQLLogFilePath = "" + AddBuiltinPermission = @{ Status = 'NotStart' StartTime = '' EndTime = '' @@ -61,80 +64,80 @@ if (Test-Path -Path $ConfigurationFile) { StartTime = '' EndTime = '' } - JoinDomain = @{ + JoinDomain = @{ Status = 'NotStart' StartTime = '' EndTime = '' } - SetAutoLogOn = @{ + SetAutoLogOn = @{ Status = 'NotStart' StartTime = '' EndTime = '' } - TurnOnFirewallPort= @{ + TurnOnFirewallPort = @{ Status = 'NotStart' StartTime = '' EndTime = '' } - InstallRolesAndFeatures= @{ + InstallRolesAndFeatures = @{ Status = 'NotStart' StartTime = '' EndTime = '' } - InstallADK= @{ + InstallADK = @{ Status = 'NotStart' StartTime = '' EndTime = '' } - ChangeSQLServicesAccount= @{ + ChangeSQLServicesAccount = @{ Status = 'NotStart' StartTime = '' EndTime = '' } - InstallSCCM= @{ + InstallSCCM = @{ Status = 'NotStart' StartTime = '' EndTime = '' } - UpgradeSCCM= @{ + UpgradeSCCM = @{ Status = 'NotStart' StartTime = '' EndTime = '' } - WaitForSiteServer= @{ + WaitForSiteServer = @{ Status = 'NotStart' StartTime = '' EndTime = '' } - InstallDP= @{ + InstallDP = @{ Status = 'NotStart' StartTime = '' EndTime = '' } - InstallMP= @{ + InstallMP = @{ Status = 'NotStart' StartTime = '' EndTime = '' } - CleanUp= @{ + CleanUp = @{ Status = 'NotStart' StartTime = '' EndTime = '' } - }; - $Configuration = New-Object -TypeName psobject -Property $Actions; + } + $Configuration = New-Object -TypeName psobject -Property $Actions } $Configuration | Add-Member -MemberType ScriptMethod -Name SetRebootConfig -Value { try - { + { $Invocation = (Get-Variable MyInvocation -Scope 1).Value $Path = $Invocation.MyCommand.Path - $BatchFilePath = Join-Path -Path $ProvisionToolPath -ChildPath "Resume_$($env:COMPUTERNAME).ps1" - $Command = "" - if($this.AddBuiltinPermission.Status -eq "Running") - { - $command = @' + $BatchFilePath = Join-Path -Path $ProvisionToolPath -ChildPath "Resume_$($env:COMPUTERNAME).ps1" + $Command = "" + if($this.AddBuiltinPermission.Status -eq "Running") + { + $command = @' Start-Sleep -Second 240 sqlcmd -Q "if not exists(select * from sys.server_principals where name='BUILTIN\administrators') CREATE LOGIN [BUILTIN\administrators] FROM WINDOWS;EXEC master..sp_addsrvrolemember @loginame = N'BUILTIN\administrators', @rolename = N'sysadmin'" $retrycount = 0 @@ -154,50 +157,50 @@ while($sqlpermission -eq $null) } } '@ - } - $Command | Out-File -FilePath $BatchFilePath -Encoding ascii - $Command = ". $Path $DCIPAddress $DomainFullName $DomainAdminName $Password $tempurl `"$sakey`"" - $Command | Out-File -FilePath $BatchFilePath -Encoding ascii -Append + } + $Command | Out-File -FilePath $BatchFilePath -Encoding ascii + $Command = ". $Path $DCIPAddress $DomainFullName $DomainAdminName $Password $tempurl `"$sakey`"" + $Command | Out-File -FilePath $BatchFilePath -Encoding ascii -Append $BatchFile = "cmd /k powershell -ExecutionPolicy Unrestricted -file " + $BatchFilePath $RunOnceRegKey = 'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce' - $KeyValueName = 'Workflow Reboot'; - $KeyType = [Microsoft.Win32.RegistryValueKind]::String; + $KeyValueName = 'Workflow Reboot' + $KeyType = [Microsoft.Win32.RegistryValueKind]::String $null = [Microsoft.Win32.Registry]::SetValue($RunOnceRegKey,$KeyValueName,$BatchFile,$KeyType) $this | ConvertTo-Json | Out-File -FilePath $ConfigurationFile -Force $configfile = $ConfigurationFile - $uploadurl = $tempurl + "/$Role.json" - AZCopy -source $configfile -dest $uploadurl -upload $true + $uploadurl = $tempurl + "/$Role.json" + AZCopy -source $configfile -dest $uploadurl -upload $true return 0 } catch - { + { return 1 } } $Mainscript = $ProvisionToolPath + "\main.ps1" -. $Mainscript; +. $Mainscript if ($Configuration.AddBuiltinPermission.Status -eq 'NotStart') { $Configuration.AddBuiltinPermission.Status = 'Running' $Configuration.AddBuiltinPermission.StartTime = Get-Date -format "yyyy-MM-dd HH:mm:ss" - Get-SQLInformation + Get-SQLInformation $Result = $Configuration.SetRebootConfig() if ($Result -eq 0) { - $Result = Set-AutoLogOn "" $DomainAdminName $Password + $Result = Set-AutoLogOn "" $DomainAdminName $Password shutdown -r -t 10 - exit 0 + exit 0 } } if ($Configuration.AddBuiltinPermission.Status -eq 'Running') { $Configuration.AddBuiltinPermission.Status = 'Completed' $Configuration.AddBuiltinPermission.EndTime = Get-Date -format "yyyy-MM-dd HH:mm:ss" - UploadConfigFile + UploadConfigFile } if ($Configuration.WaitForDC.Status -eq 'NotStart') { @@ -251,7 +254,7 @@ if($Configuration.WaitForDC.Status -eq 'Completed') $Result = $Configuration.SetRebootConfig() if ($Result -eq 0) { shutdown -r -t 0 - exit 0 + exit 0 } } else @@ -265,17 +268,17 @@ if($Configuration.WaitForDC.Status -eq 'Completed') Enable-RDP - if ($Configuration.TurnOnFirewallPort.Status -eq 'NotStart') { - $Configuration.TurnOnFirewallPort.Status = 'Running' - $Configuration.TurnOnFirewallPort.StartTime = Get-Date -format "yyyy-MM-dd HH:mm:ss" - UploadConfigFile - $Result = TurnOn-FirewallPort $RoleList - if ($Result[-1] -eq 0) { - $Configuration.TurnOnFirewallPort.Status = 'Completed' - $Configuration.TurnOnFirewallPort.EndTime = Get-Date -format "yyyy-MM-dd HH:mm:ss" - } - UploadConfigFile - } + if ($Configuration.TurnOnFirewallPort.Status -eq 'NotStart') { + $Configuration.TurnOnFirewallPort.Status = 'Running' + $Configuration.TurnOnFirewallPort.StartTime = Get-Date -format "yyyy-MM-dd HH:mm:ss" + UploadConfigFile + $Result = TurnOn-FirewallPort $RoleList + if ($Result[-1] -eq 0) { + $Configuration.TurnOnFirewallPort.Status = 'Completed' + $Configuration.TurnOnFirewallPort.EndTime = Get-Date -format "yyyy-MM-dd HH:mm:ss" + } + UploadConfigFile + } if ($Configuration.InstallRolesAndFeatures.Status -eq 'NotStart') { $Configuration.InstallRolesAndFeatures.Status = 'Running' @@ -285,12 +288,12 @@ if($Configuration.WaitForDC.Status -eq 'Completed') if ($Result[-1] -eq 0) { $Configuration.InstallRolesAndFeatures.Status = 'Completed' $Configuration.InstallRolesAndFeatures.EndTime = Get-Date -format "yyyy-MM-dd HH:mm:ss" - $Result = $Configuration.SetRebootConfig() - $Result = Set-AutoLogOn $DomainFullName $DomainAdminName $Password - if ($Result -eq 0) { - shutdown -r -t 10 - exit 0 - } + $Result = $Configuration.SetRebootConfig() + $Result = Set-AutoLogOn $DomainFullName $DomainAdminName $Password + if ($Result -eq 0) { + shutdown -r -t 10 + exit 0 + } } } @@ -305,10 +308,10 @@ if($Configuration.WaitForDC.Status -eq 'Completed') $Configuration.InstallADK.Status = 'Completed' $Configuration.InstallADK.EndTime = Get-Date -format "yyyy-MM-dd HH:mm:ss" } - UploadConfigFile + UploadConfigFile } - - if ($Configuration.ChangeSQLServicesAccount.Status -eq 'NotStart') { + + if ($Configuration.ChangeSQLServicesAccount.Status -eq 'NotStart') { $Configuration.ChangeSQLServicesAccount.Status = 'Running' $Configuration.ChangeSQLServicesAccount.StartTime = Get-Date -format "yyyy-MM-dd HH:mm:ss" UploadConfigFile @@ -317,11 +320,11 @@ if($Configuration.WaitForDC.Status -eq 'Completed') $Configuration.ChangeSQLServicesAccount.Status = 'Completed' $Configuration.ChangeSQLServicesAccount.EndTime = Get-Date -format "yyyy-MM-dd HH:mm:ss" } - UploadConfigFile + UploadConfigFile } ##Install CM - if ($Configuration.InstallSCCM.Status -eq 'NotStart') { + if ($Configuration.InstallSCCM.Status -eq 'NotStart') { $Configuration.InstallSCCM.Status = 'Running' $Configuration.InstallSCCM.StartTime = Get-Date -format "yyyy-MM-dd HH:mm:ss" UploadConfigFile @@ -330,69 +333,89 @@ if($Configuration.WaitForDC.Status -eq 'Completed') $Configuration.InstallSCCM.Status = 'Completed' $Configuration.InstallSCCM.EndTime = Get-Date -format "yyyy-MM-dd HH:mm:ss" - $Result = $Configuration.SetRebootConfig() - $Result = Set-AutoLogOn $DomainFullName $DomainAdminName $Password + $Result = $Configuration.SetRebootConfig() + $Result = Set-AutoLogOn $DomainFullName $DomainAdminName $Password if ($Result -eq 0) { shutdown -r -t 120 } } - UploadConfigFile - exit 0 + else + { + $Configuration.InstallSCCM.Status = 'Error' + $Configuration.InstallSCCM.EndTime = Get-Date -format "yyyy-MM-dd HH:mm:ss" + } + UploadConfigFile + exit 0 } - if($Configuration.InstallSCCM.Status -eq 'Completed') - { - if ($Configuration.UpgradeSCCM.Status -eq 'NotStart') - { - $Configuration.UpgradeSCCM.Status = 'Running' - $Configuration.UpgradeSCCM.StartTime = Get-Date -format "yyyy-MM-dd HH:mm:ss" - UploadConfigFile - $Result = Upgrade-SCCM $DomainFullName - if ($Result[-1] -eq 0) { - $Configuration.UpgradeSCCM.Status = 'Completed' - $Configuration.UpgradeSCCM.EndTime = Get-Date -format "yyyy-MM-dd HH:mm:ss" - } - UploadConfigFile - } - - if ($Configuration.WaitForSiteServer.Status -eq 'NotStart') { - $Configuration.WaitForSiteServer.Status = 'Running' - $Configuration.WaitForSiteServer.StartTime = Get-Date -format "yyyy-MM-dd HH:mm:ss" - UploadConfigFile - $Result = WaitFor-SiteServer "DP_MP" - if ($Result[-1] -eq 0) { - $Configuration.WaitForSiteServer.Status = 'Completed' - $Configuration.WaitForSiteServer.EndTime = Get-Date -format "yyyy-MM-dd HH:mm:ss" - } - UploadConfigFile - } - - if ($Configuration.InstallDP.Status -eq 'NotStart') { - $Configuration.InstallDP.Status = 'Running' - $Configuration.InstallDP.StartTime = Get-Date -format "yyyy-MM-dd HH:mm:ss" - UploadConfigFile - $Result = Install-DP $DomainFullName - if ($Result[-1] -eq 0) { - $Configuration.InstallDP.Status = 'Completed' - $Configuration.InstallDP.EndTime = Get-Date -format "yyyy-MM-dd HH:mm:ss" - } - UploadConfigFile - } - - if ($Configuration.InstallMP.Status -eq 'NotStart') { - $Configuration.InstallMP.Status = 'Running' - $Configuration.InstallMP.StartTime = Get-Date -format "yyyy-MM-dd HH:mm:ss" - UploadConfigFile - $Result = Install-MP $DomainFullName $Role $DomainAdminName $Password - if ($Result[-1] -eq 0) { - $Configuration.InstallMP.Status = 'Completed' - $Configuration.InstallMP.EndTime = Get-Date -format "yyyy-MM-dd HH:mm:ss" - } - UploadConfigFile - } - } - - if ($Configuration.CleanUp.Status -eq 'NotStart') { + if($Configuration.InstallSCCM.Status -eq 'Completed') + { + if ($Configuration.UpgradeSCCM.Status -eq 'NotStart') + { + $Configuration.UpgradeSCCM.Status = 'Running' + $Configuration.UpgradeSCCM.StartTime = Get-Date -format "yyyy-MM-dd HH:mm:ss" + UploadConfigFile + $Result = Upgrade-SCCM $DomainFullName + if ($Result[-1] -eq 0) { + $Configuration.UpgradeSCCM.Status = 'Completed' + $Configuration.UpgradeSCCM.EndTime = Get-Date -format "yyyy-MM-dd HH:mm:ss" + } + else + { + $Configuration.UpgradeSCCM.Status = 'Error' + $Configuration.UpgradeSCCM.EndTime = Get-Date -format "yyyy-MM-dd HH:mm:ss" + } + UploadConfigFile + } + + if ($Configuration.WaitForSiteServer.Status -eq 'NotStart') { + $Configuration.WaitForSiteServer.Status = 'Running' + $Configuration.WaitForSiteServer.StartTime = Get-Date -format "yyyy-MM-dd HH:mm:ss" + UploadConfigFile + $Result = WaitFor-SiteServer "DP_MP" + if ($Result[-1] -eq 0) { + $Configuration.WaitForSiteServer.Status = 'Completed' + $Configuration.WaitForSiteServer.EndTime = Get-Date -format "yyyy-MM-dd HH:mm:ss" + } + UploadConfigFile + } + + if ($Configuration.InstallDP.Status -eq 'NotStart') { + $Configuration.InstallDP.Status = 'Running' + $Configuration.InstallDP.StartTime = Get-Date -format "yyyy-MM-dd HH:mm:ss" + UploadConfigFile + $Result = Install-DP $DomainFullName + if ($Result[-1] -eq 0) { + $Configuration.InstallDP.Status = 'Completed' + $Configuration.InstallDP.EndTime = Get-Date -format "yyyy-MM-dd HH:mm:ss" + } + else + { + $Configuration.InstallDP.Status = 'Error' + $Configuration.InstallDP.EndTime = Get-Date -format "yyyy-MM-dd HH:mm:ss" + } + UploadConfigFile + } + + if ($Configuration.InstallMP.Status -eq 'NotStart') { + $Configuration.InstallMP.Status = 'Running' + $Configuration.InstallMP.StartTime = Get-Date -format "yyyy-MM-dd HH:mm:ss" + UploadConfigFile + $Result = Install-MP $DomainFullName $Role $DomainAdminName $Password + if ($Result[-1] -eq 0) { + $Configuration.InstallMP.Status = 'Completed' + $Configuration.InstallMP.EndTime = Get-Date -format "yyyy-MM-dd HH:mm:ss" + } + else + { + $Configuration.InstallMP.Status = 'Error' + $Configuration.InstallMP.EndTime = Get-Date -format "yyyy-MM-dd HH:mm:ss" + } + UploadConfigFile + } + } + + if ($Configuration.CleanUp.Status -eq 'NotStart') { $Configuration.CleanUp.Status = 'Running' $Configuration.CleanUp.StartTime = Get-Date -format "yyyy-MM-dd HH:mm:ss" UploadConfigFile @@ -401,7 +424,7 @@ if($Configuration.WaitForDC.Status -eq 'Completed') $Configuration.CleanUp.Status = 'Completed' $Configuration.CleanUp.EndTime = Get-Date -format "yyyy-MM-dd HH:mm:ss" } - UploadConfigFile + UploadConfigFile } } } diff --git a/sccm-technicalpreview/scripts/main.ps1 b/sccm-technicalpreview/scripts/main.ps1 index 93ccda1e9e2d..ebdf33a8e458 100644 --- a/sccm-technicalpreview/scripts/main.ps1 +++ b/sccm-technicalpreview/scripts/main.ps1 @@ -16,28 +16,28 @@ function Install-ADDS($DomainFullName,$Password) function Add-BuiltinPermission { - $logpath = $ProvisionToolPath+"\AddBuiltinPermission.txt" - try - { - Start-Sleep -Seconds 240 - "[$(Get-Date -format HH:mm:ss)] Adding Built Permission ..." | Out-File -Append $logpath - sqlcmd -Q "if not exists(select * from sys.server_principals where name='BUILTIN\administrators') CREATE LOGIN [BUILTIN\administrators] FROM WINDOWS;EXEC master..sp_addsrvrolemember @loginame = N'BUILTIN\administrators', @rolename = N'sysadmin'" | Out-Null - - $returncode = sqlcmd -Q "if not exists(select * from sys.server_principals where name='BUILTIN\administrators') PRINT 1" - while($returncode -eq 1 -or $returncode -eq $null) - { - "[$(Get-Date -format HH:mm:ss)] Failed , will try again ..." | Out-File -Append $logpath - Start-Sleep -Seconds 60 - sqlcmd -Q "if not exists(select * from sys.server_principals where name='BUILTIN\administrators') CREATE LOGIN [BUILTIN\administrators] FROM WINDOWS;EXEC master..sp_addsrvrolemember @loginame = N'BUILTIN\administrators', @rolename = N'sysadmin'" | Out-Null - $returncode = sqlcmd -Q "if not exists(select * from sys.server_principals where name='BUILTIN\administrators') PRINT 1" - } - } - catch - { - $_.Exception | Out-File -Append $logpath - Return 1 - } - Return 0 + $logpath = $ProvisionToolPath+"\AddBuiltinPermission.txt" + try + { + Start-Sleep -Seconds 240 + "[$(Get-Date -format HH:mm:ss)] Adding Built Permission ..." | Out-File -Append $logpath + sqlcmd -Q "if not exists(select * from sys.server_principals where name='BUILTIN\administrators') CREATE LOGIN [BUILTIN\administrators] FROM WINDOWS;EXEC master..sp_addsrvrolemember @loginame = N'BUILTIN\administrators', @rolename = N'sysadmin'" | Out-Null + + $returncode = sqlcmd -Q "if not exists(select * from sys.server_principals where name='BUILTIN\administrators') PRINT 1" + while($returncode -eq 1 -or $returncode -eq $null) + { + "[$(Get-Date -format HH:mm:ss)] Failed , will try again ..." | Out-File -Append $logpath + Start-Sleep -Seconds 60 + sqlcmd -Q "if not exists(select * from sys.server_principals where name='BUILTIN\administrators') CREATE LOGIN [BUILTIN\administrators] FROM WINDOWS;EXEC master..sp_addsrvrolemember @loginame = N'BUILTIN\administrators', @rolename = N'sysadmin'" | Out-Null + $returncode = sqlcmd -Q "if not exists(select * from sys.server_principals where name='BUILTIN\administrators') PRINT 1" + } + } + catch + { + $_.Exception | Out-File -Append $logpath + Return 1 + } + Return 0 } function Set-AutoLogOn($DomainFullName,$Username,$Password) @@ -127,7 +127,7 @@ function WaitFor-DC $waitfordclog = $ProvisionToolPath +"\WaitForDCLog.txt" $dcconfigpath = $ProvisionToolPath + "\DC.json" $source = $tempurl + "/DC.json" - AZCopy $source $dcconfigpath $false + AZCopy $source $dcconfigpath $false while(!(Test-Path $dcconfigpath)) { "[$(Get-Date -format HH:mm:ss)] Waiting for DC config file..." | Out-File -Append $waitfordclog @@ -152,7 +152,7 @@ function WaitFor-PS $waitforpslog = $ProvisionToolPath +"\WaitForPSLog.txt" $psconfigpath = $ProvisionToolPath + "\PS1.json" $source = $tempurl + "/PS1.json" - AZCopy $source $psconfigpath $false + AZCopy $source $psconfigpath $false while(!(Test-Path $psconfigpath)) { "[$(Get-Date -format HH:mm:ss)] Waiting for PS config file..." | Out-File -Append $waitforpslog @@ -177,7 +177,7 @@ function WaitFor-SiteServer($SiteServerRole) $waitforsiteserverlog = $ProvisionToolPath +"\WaitForSiteServerLog.txt" $siteserverconfigpath = $ProvisionToolPath + "\$SiteServerRole.json" $source = $tempurl + "/$SiteServerRole.json" - AZCopy $source $siteserverconfigpath $false + AZCopy $source $siteserverconfigpath $false while(!(Test-Path $siteserverconfigpath)) { "[$(Get-Date -format HH:mm:ss)] Waiting for Site Server config file..." | Out-File -Append $waitforsiteserverlog @@ -202,7 +202,7 @@ function WaitFor-SQL $waitforsqllog = $ProvisionToolPath +"\waitforsqllog.txt" $sqlconfigpath = $ProvisionToolPath + "\SQL.json" $source = $tempurl + "/SQL.json" - AZCopy $source $sqlconfigpath $false + AZCopy $source $sqlconfigpath $false while(!(Test-Path $sqlconfigpath)) { "[$(Get-Date -format HH:mm:ss)] Waiting for SQL config file..." | Out-File -Append $waitforsqllog @@ -227,9 +227,9 @@ function UploadConfigFile { $Configuration | ConvertTo-Json | Out-File -FilePath $ConfigurationFile -Force; $configfile = $ConfigurationFile - $uploadurl = $tempurl + "/$Role.json" + $uploadurl = $tempurl + "/$Role.json" - AZCopy $configfile $uploadurl $true + AZCopy $configfile $uploadurl $true } function Enable-RDP @@ -265,48 +265,48 @@ function TurnOn-FirewallPort([string[]]$Role) function Delegate-Control($DomainFullName,[string[]]$RoleList) { - $logpath = $ProvisionToolPath+"\DelegateControlLog.txt" - $RoleList | %{ - "[$(Get-Date -format HH:mm:ss)] Add permission for $_ ..." | Out-File -Append $logpath - $psconfigpath = $ProvisionToolPath + "\$_.json" - $source = $tempurl + "/$_.json" - AZCopy $source $psconfigpath $false - $psconfig = gc $psconfigpath | ConvertFrom-Json - - $currentmachine = $psconfig.Name - - try - { - $root = ConfigContainer - $DomainName = $DomainFullName.split('.')[0] - #Delegate Control - $cmd = "dsacls.exe" - $arg1 = "CN=System Management,CN=System,$root" - $arg2 = "/G" - $arg3 = ""+$DomainName+"\"+$currentmachine+"`$:GA;;" - $arg4 = "/I:T" - - & $cmd $arg1 $arg2 $arg3 $arg4 - "[$(Get-Date -format HH:mm:ss)] Finished." | Out-File -Append $logpath - } - catch - { - return 1 - } - } + $logpath = $ProvisionToolPath+"\DelegateControlLog.txt" + $RoleList | %{ + "[$(Get-Date -format HH:mm:ss)] Add permission for $_ ..." | Out-File -Append $logpath + $psconfigpath = $ProvisionToolPath + "\$_.json" + $source = $tempurl + "/$_.json" + AZCopy $source $psconfigpath $false + $psconfig = gc $psconfigpath | ConvertFrom-Json + + $currentmachine = $psconfig.Name + + try + { + $root = ConfigContainer + $DomainName = $DomainFullName.split('.')[0] + #Delegate Control + $cmd = "dsacls.exe" + $arg1 = "CN=System Management,CN=System,$root" + $arg2 = "/G" + $arg3 = ""+$DomainName+"\"+$currentmachine+"`$:GA;;" + $arg4 = "/I:T" + + & $cmd $arg1 $arg2 $arg3 $arg4 + "[$(Get-Date -format HH:mm:ss)] Finished." | Out-File -Append $logpath + } + catch + { + return 1 + } + } - Return 0 + Return 0 } function Extend-ADSchema { - $url = "https://cmsetoolstorage.blob.core.windows.net/work/tools/extadsch.exe" + $url = "https://cmsetoolstorage.blob.core.windows.net/work/tools/extadsch.exe" $path = "C:\extadsch.exe" Invoke-WebRequest -Uri $url -OutFile $path & 'C:\extadsch.exe' | out-null - return 0 + return 0 } function Install-ADK @@ -343,15 +343,15 @@ function Install-SCCM($DomainFullName,$Username,$Password,$SQLRole,$CM) { $path = "$ProvisionToolPath\InstallSCCM.ps1" - #GetSQLInfo - $configpath = $ProvisionToolPath + "\$SQLRole.json" + #GetSQLInfo + $configpath = $ProvisionToolPath + "\$SQLRole.json" $source = $tempurl + "/$SQLRole.json" - AZCopy $source $configpath $false + AZCopy $source $configpath $false $config = gc $configpath | ConvertFrom-Json - $SQLVMName = $config.Name - $SQLInstanceName = $config.SQLInstanceName - $SQLDataFilePath = $config.SQLDataFilePath - $SQLLogFilePath = $config.SQLLogFilePath + $SQLVMName = $config.Name + $SQLInstanceName = $config.SQLInstanceName + $SQLDataFilePath = $config.SQLDataFilePath + $SQLLogFilePath = $config.SQLLogFilePath try { @@ -381,97 +381,97 @@ function Upgrade-SCCM($DomainFullName) function Update-SQLServicesAccount($DomainFullName,$Username,$Password) { - if($DomainFullName) - { - $NetBIOSName = $DomainFullName.split('.')[0] - $Username = $NetBIOSName + '\' + $Username - } - $DomainPassword = $Password - $DomainUserName = $Username - - $SQLInstanceName = $Configuration.SQLInstanceName - $logpath = $ProvisionToolPath + "\UpdateSQLServicesAccount.log" - #Get SQL Server Services account - $query = "Name = '"+ $SQLInstanceName.ToUpper() +"'" - $services = Get-WmiObject win32_service -Filter $query - if($services -ne $null) - { - "[$(Get-Date -format HH:mm:ss)] Verify if SQL Server services account need to be changed" | Out-File -Append $logpath - if($services.StartName -ne $DomainUserName) - { - "[$(Get-Date -format HH:mm:ss)] SQL Server services account need to be changed" | Out-File -Append $logpath - #change services account - if($services.State -eq 'Running') - { - #Check if SQLSERVERAGENT is running - $sqlserveragentflag = 0 - $sqlserveragentservices = Get-WmiObject win32_service -Filter "Name = 'SQLSERVERAGENT'" - if($sqlserveragentservices -ne $null) - { - if($sqlserveragentservices.State -eq 'Running') - { - "[$(Get-Date -format HH:mm:ss)] SQLSERVERAGENT need to be stopped first" | Out-File -Append $logpath - $Result = $sqlserveragentservices.StopService() - "[$(Get-Date -format HH:mm:ss)] Stopping SQLSERVERAGENT.." | Out-File -Append $logpath - if ($Result.ReturnValue -eq '0') - { - $sqlserveragentflag = 1 - "[$(Get-Date -format HH:mm:ss)] Stopped" | Out-File -Append $logpath - } - } - } - $Result = $services.StopService() - "[$(Get-Date -format HH:mm:ss)] Stopping SQL Server services.." | Out-File -Append $logpath - if ($Result.ReturnValue -eq '0') - { - "[$(Get-Date -format HH:mm:ss)] Stopped" | Out-File -Append $logpath - } - - "[$(Get-Date -format HH:mm:ss)] Changing the services account..." | Out-File -Append $logpath - - $Result = $services.change($null,$null,$null,$null,$null,$null,$DomainUserName,$Password,$null,$null,$null) - if ($Result.ReturnValue -eq '0') - { - "[$(Get-Date -format HH:mm:ss)] Successfully Change the services account" | Out-File -Append $logpath - if($sqlserveragentflag -eq 1) - { - "[$(Get-Date -format HH:mm:ss)] Starting SQLSERVERAGENT.." | Out-File -Append $logpath - $Result = $sqlserveragentservices.StartService() - if($Result.ReturnValue -eq '0') - { - "[$(Get-Date -format HH:mm:ss)] Started" | Out-File -Append $logpath - } - } - $Result = $services.StartService() - "[$(Get-Date -format HH:mm:ss)] Starting SQL Server services.." | Out-File -Append $logpath - while($Result.ReturnValue -ne '0') - { - $returncode = $Result.ReturnValue - "[$(Get-Date -format HH:mm:ss)] Return $returncode , will try again" | Out-File -Append $logpath - Start-Sleep -Seconds 10 - $Result = $services.StartService() - } - "[$(Get-Date -format HH:mm:ss)] Started" | Out-File -Append $logpath - } - } - } - else - { - "[$(Get-Date -format HH:mm:ss)] No need to be changed" | Out-File -Append $logpath - } - } - Return 0 + if($DomainFullName) + { + $NetBIOSName = $DomainFullName.split('.')[0] + $Username = $NetBIOSName + '\' + $Username + } + $DomainPassword = $Password + $DomainUserName = $Username + + $SQLInstanceName = $Configuration.SQLInstanceName + $logpath = $ProvisionToolPath + "\UpdateSQLServicesAccount.log" + #Get SQL Server Services account + $query = "Name = '"+ $SQLInstanceName.ToUpper() +"'" + $services = Get-WmiObject win32_service -Filter $query + if($services -ne $null) + { + "[$(Get-Date -format HH:mm:ss)] Verify if SQL Server services account need to be changed" | Out-File -Append $logpath + if($services.StartName -ne $DomainUserName) + { + "[$(Get-Date -format HH:mm:ss)] SQL Server services account need to be changed" | Out-File -Append $logpath + #change services account + if($services.State -eq 'Running') + { + #Check if SQLSERVERAGENT is running + $sqlserveragentflag = 0 + $sqlserveragentservices = Get-WmiObject win32_service -Filter "Name = 'SQLSERVERAGENT'" + if($sqlserveragentservices -ne $null) + { + if($sqlserveragentservices.State -eq 'Running') + { + "[$(Get-Date -format HH:mm:ss)] SQLSERVERAGENT need to be stopped first" | Out-File -Append $logpath + $Result = $sqlserveragentservices.StopService() + "[$(Get-Date -format HH:mm:ss)] Stopping SQLSERVERAGENT.." | Out-File -Append $logpath + if ($Result.ReturnValue -eq '0') + { + $sqlserveragentflag = 1 + "[$(Get-Date -format HH:mm:ss)] Stopped" | Out-File -Append $logpath + } + } + } + $Result = $services.StopService() + "[$(Get-Date -format HH:mm:ss)] Stopping SQL Server services.." | Out-File -Append $logpath + if ($Result.ReturnValue -eq '0') + { + "[$(Get-Date -format HH:mm:ss)] Stopped" | Out-File -Append $logpath + } + + "[$(Get-Date -format HH:mm:ss)] Changing the services account..." | Out-File -Append $logpath + + $Result = $services.change($null,$null,$null,$null,$null,$null,$DomainUserName,$Password,$null,$null,$null) + if ($Result.ReturnValue -eq '0') + { + "[$(Get-Date -format HH:mm:ss)] Successfully Change the services account" | Out-File -Append $logpath + if($sqlserveragentflag -eq 1) + { + "[$(Get-Date -format HH:mm:ss)] Starting SQLSERVERAGENT.." | Out-File -Append $logpath + $Result = $sqlserveragentservices.StartService() + if($Result.ReturnValue -eq '0') + { + "[$(Get-Date -format HH:mm:ss)] Started" | Out-File -Append $logpath + } + } + $Result = $services.StartService() + "[$(Get-Date -format HH:mm:ss)] Starting SQL Server services.." | Out-File -Append $logpath + while($Result.ReturnValue -ne '0') + { + $returncode = $Result.ReturnValue + "[$(Get-Date -format HH:mm:ss)] Return $returncode , will try again" | Out-File -Append $logpath + Start-Sleep -Seconds 10 + $Result = $services.StartService() + } + "[$(Get-Date -format HH:mm:ss)] Started" | Out-File -Append $logpath + } + } + } + else + { + "[$(Get-Date -format HH:mm:ss)] No need to be changed" | Out-File -Append $logpath + } + } + Return 0 } function Install-DP($DomainFullName) { $path = "$ProvisionToolPath\InstallDP.ps1" - $configpath = $ProvisionToolPath + "\DP_MP.json" + $configpath = $ProvisionToolPath + "\DP_MP.json" $source = $tempurl + "/DP_MP.json" - AZCopy $source $configpath $false + AZCopy $source $configpath $false $config = gc $configpath | ConvertFrom-Json - $currentmachine = $config.Name + $currentmachine = $config.Name try { @@ -486,8 +486,8 @@ function Install-DP($DomainFullName) function Clean-Up { - $BatchFilePath = Join-Path -Path $ProvisionToolPath -ChildPath "Resume_$($env:COMPUTERNAME).ps1" - Remove-Item $BatchFilePath + $BatchFilePath = Join-Path -Path $ProvisionToolPath -ChildPath "Resume_$($env:COMPUTERNAME).ps1" + Remove-Item $BatchFilePath return 0 } @@ -495,21 +495,21 @@ function Clean-Up function Install-MP($DomainFullName,$SQLRole,$DomainAdminName,$Password) { $path = "$ProvisionToolPath\InstallMP.ps1" - $configpath = $ProvisionToolPath + "\DP_MP.json" + $configpath = $ProvisionToolPath + "\DP_MP.json" $source = $tempurl + "/DP_MP.json" - AZCopy $source $configpath $false + AZCopy $source $configpath $false $config = gc $configpath | ConvertFrom-Json - $currentmachine = $config.Name + $currentmachine = $config.Name - #GetSQLInfo - $configpath = $ProvisionToolPath + "\$SQLRole.json" + #GetSQLInfo + $configpath = $ProvisionToolPath + "\$SQLRole.json" $source = $tempurl + "/$SQLRole.json" - AZCopy $source $configpath $false + AZCopy $source $configpath $false $config = gc $configpath | ConvertFrom-Json - $SQLVMName = $config.Name - $SQLInstanceName = $config.SQLInstanceName - $SQLDataFilePath = $config.SQLDataFilePath - $SQLLogFilePath = $config.SQLLogFilePath + $SQLVMName = $config.Name + $SQLInstanceName = $config.SQLInstanceName + $SQLDataFilePath = $config.SQLDataFilePath + $SQLLogFilePath = $config.SQLLogFilePath try { @@ -546,17 +546,17 @@ function ConfigContainer function Add-Permission($DomainFullName) { - $logpath = $ProvisionToolPath+"\AddPermissionLog.txt" - $DomainName = $DomainFullName.split('.')[0] - $psconfigpath = $ProvisionToolPath + "\PS1.json" + $logpath = $ProvisionToolPath+"\AddPermissionLog.txt" + $DomainName = $DomainFullName.split('.')[0] + $psconfigpath = $ProvisionToolPath + "\PS1.json" $source = $tempurl + "/PS1.json" - AZCopy $source $psconfigpath $false + AZCopy $source $psconfigpath $false $psconfig = gc $psconfigpath | ConvertFrom-Json - $currentmachine = $psconfig.Name - $psname = $currentmachine +"$" + $currentmachine = $psconfig.Name + $psname = $currentmachine +"$" - $GroupObj = [ADSI]"WinNT://$env:COMPUTERNAME/Administrators" + $GroupObj = [ADSI]"WinNT://$env:COMPUTERNAME/Administrators" if($GroupObj.IsMember("WinNT://$DomainName/$psname") -eq $false) { "[$(Get-Date -format HH:mm:ss)] add $psname to administrators group" | Out-File -Append $logpath @@ -567,17 +567,17 @@ function Add-Permission($DomainFullName) "[$(Get-Date -format HH:mm:ss)] $psname is already in administrators group" | Out-File -Append $logpath } - return 0 + return 0 } function Get-SQLInformation { - $inst = (get-itemproperty 'HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server').InstalledInstances[0] - $p = (Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server\Instance Names\SQL').$inst + $inst = (get-itemproperty 'HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server').InstalledInstances[0] + $p = (Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server\Instance Names\SQL').$inst - $sqlinfo = Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server\$p\$inst" + $sqlinfo = Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server\$p\$inst" - $Configuration.SQLInstanceName = $inst - $Configuration.SQLDataFilePath = $sqlinfo.DefaultData - $Configuration.SQLLogFilePath = $sqlinfo.DefaultLog + $Configuration.SQLInstanceName = $inst + $Configuration.SQLDataFilePath = $sqlinfo.DefaultData + $Configuration.SQLLogFilePath = $sqlinfo.DefaultLog } \ No newline at end of file diff --git a/test/README.md b/test/README.md deleted file mode 100644 index 8b137891791f..000000000000 --- a/test/README.md +++ /dev/null @@ -1 +0,0 @@ - diff --git a/test/template-tests/AzRMTester.psd1 b/test/template-tests/AzRMTester.psd1 new file mode 100644 index 000000000000..cdafb19677f9 --- /dev/null +++ b/test/template-tests/AzRMTester.psd1 @@ -0,0 +1,5 @@ +@{ + ModuleVersion = 0.1 + ModuleToProcess = 'AzRMTester.psm1' + Description = 'Validation tools for Azure Resource Manager Templates' +} \ No newline at end of file diff --git a/test/template-tests/AzRMTester.psm1 b/test/template-tests/AzRMTester.psm1 new file mode 100644 index 000000000000..c56ec4c0681c --- /dev/null +++ b/test/template-tests/AzRMTester.psm1 @@ -0,0 +1,6 @@ +. $psScriptRoot\ConvertFrom-Json.ps1 # Overwriting ConvertFrom-JSON to allow for comments within JSON + +. $PSScriptRoot\Expand-AzureRMTemplate.ps1 +. $PSScriptRoot\Test-AzureRMTemplate.ps1 + + diff --git a/test/template-tests/ConvertFrom-JSON.ps1 b/test/template-tests/ConvertFrom-JSON.ps1 new file mode 100644 index 000000000000..c0f4215a1dc9 --- /dev/null +++ b/test/template-tests/ConvertFrom-JSON.ps1 @@ -0,0 +1,61 @@ +function ConvertFrom-Json +{ +[CmdletBinding(HelpUri='https://go.microsoft.com/fwlink/?LinkID=217031', RemotingCapability='None')] +param( + [Parameter(Mandatory=$true, Position=0, ValueFromPipeline=$true)] + [AllowEmptyString()] + [string] + ${InputObject}) + +begin +{ + try { + $outBuffer = $null + if ($PSBoundParameters.TryGetValue('OutBuffer', [ref]$outBuffer)) + { + $PSBoundParameters['OutBuffer'] = 1 + } + $wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand('Microsoft.PowerShell.Utility\ConvertFrom-Json', [System.Management.Automation.CommandTypes]::Cmdlet) + $scriptCmd = {& $wrappedCmd @PSBoundParameters } + $steppablePipeline = $scriptCmd.GetSteppablePipeline($myInvocation.CommandOrigin) + $steppablePipeline.Begin($PSCmdlet) + } catch { + throw + } +} + +process +{ + try { + $options = 'Multiline,IgnoreCase,IgnorePatternWhitespace' + + $in = + [Regex]::Replace( + [Regex]::Replace($_, '/\*(?[^(/*/)]+)\*/', '', $options), + '(?.{0,})$', '', $options + ) + + $steppablePipeline.Process( + $in + ) + } catch { + throw + } +} + +end +{ + try { + $steppablePipeline.End() + } catch { + throw + } +} +<# + +.ForwardHelpTargetName Microsoft.PowerShell.Utility\ConvertFrom-Json +.ForwardHelpCategory Cmdlet + +#> + +} \ No newline at end of file diff --git a/test/template-tests/Expand-AzureRMTemplate.ps1 b/test/template-tests/Expand-AzureRMTemplate.ps1 new file mode 100644 index 000000000000..3893d5bbe728 --- /dev/null +++ b/test/template-tests/Expand-AzureRMTemplate.ps1 @@ -0,0 +1,182 @@ +function Expand-AzureRMTemplate +{ + <# + .Synopsis + Expands the contents of an Azure Resource Manager template. + .Description + Expands an Azure Resource Manager template and related files into a set of well-known parameters + + #> + param( + # The path to an Azure resource manager template + [Parameter(Mandatory=$true,Position=0,ValueFromPipelineByPropertyName=$true,ParameterSetName='SpecificTemplate')] + [Alias('Fullname','Path')] + [string] + $TemplatePath + ) + + begin { + function Expand-Resource ( + [Parameter(Mandatory=$true,Position=0,ValueFromPipelineByPropertyName=$true)] + [Alias('Resources')] + [PSObject[]] + $Resource, + + [PSObject[]] + $Parent + ) { + process { + foreach ($r in $Resource) { + $r | + Add-Member NoteProperty ParentResources $parent -Force -PassThru + + if ($r.resources) { + $r | Expand-Resource -Parent (@($r) + @(if ($parent) { $parent })) + } + } + } + } + } + + process { + # Now let's try to resolve the template path. + $resolvedTemplatePath = + # If the template path doesn't appear to be a path to a json file, + if ($TemplatePath -notlike '*.json') { + # see if it looks like a file + if (($templatePath | Split-Path -Leaf) -like '*.*') { + $TemplatePath = $TemplatePath | Split-Path # if it does, reassign template path to it's directory. + } + # Then, go looking beneath that template path + $preferredJsonFile = $TemplatePath | + Get-ChildItem -Filter *.json | + # for a file named azureDeploy.json or mainTemplate.json + Where-Object { 'azureDeploy.json', 'mainTemplate.json' -contains $_.Name } | + Select-Object -First 1 -ExpandProperty Fullname + # If no file was found, write an error and return. + if (-not $preferredJsonFile) { + Write-Error "No azureDeploy.json or mainTemplate.json found beneath $TemplatePath" + return + } + $preferredJsonFile + } else { + $ExecutionContext.SessionState.Path.GetResolvedPSPathFromPSPath($templatePath) + } + + # If we couldn't find a template file, return (an error should have already been written). + if (-not $resolvedTemplatePath) { return } + + + # Next, we want to pre-populate a number of well-known variables. + # These variables will be available to every test case. They are: + $WellKnownVariables = 'TemplateFullPath','TemplateText','TemplateObject', + 'CreateUIDefinitionFullPath','createUIDefintionText','CreateUIDefinitionObject', + 'FolderName', 'HasCreateUIDefinition', 'IsMainTemplate','FolderFiles', + 'MainTemplatePath', 'MainTemplateObject', 'MainTemplateText' + foreach ($_ in $WellKnownVariables) { + $ExecutionContext.SessionState.PSVariable.Set($_, $null) + } + + #*$templateFullPath (the full path to the .json file) + $TemplateFullPath = "$resolvedTemplatePath" + #*$TemplateFileName (the name of the azure template file) + $templateFileName = $TemplatePath | Split-Path -Leaf + #*$IsMainTemplate (if the TemplateFileName is named mainTemplate.json) + $isMainTemplate = 'mainTemplate.json', 'azureDeploy.json' -contains $templateFileName + $templateFile = Get-Item -LiteralPath $resolvedTemplatePath + $templateFolder = $templateFile.Directory + #*$FolderName (the name of the root folder containing the template) + $TemplateName = $templateFolder.Name + #*$TemplateText (the text contents of the template file) + $TemplateText = [IO.File]::ReadAllText($resolvedTemplatePath) + #*$TemplateObject (the template text, converted from JSON) + $TemplateObject = $TemplateText | ConvertFrom-Json + #*$CreateUIDefinitionFullPath (the path to CreateUIDefinition.json) + $createUiDefinitionFullPath = Join-Path -childPath 'createUiDefinition.json' -Path $templateFolder + if (Test-Path $createUiDefinitionFullPath) { + #*$CreateUIDefinitionText (the text contents of CreateUIDefinition.json) + $createUIDefintionText = [IO.File]::ReadAllText($createUiDefinitionFullPath) + #*$CreateUIDefinitionObject (the createuidefinition text, converted from json) + $createUIDefinitionObject = $createUIDefintionText | ConvertFrom-Json + #*$HasCreateUIDefinition (indicates if a CreateUIDefinition.json file exists) + $HasCreateUIDefinition = $true + } else { + $HasCreateUIDefinition = $false + $createUiDefinitionFullPath = $null + } + + + #*$FolderFiles (a list of objects of each file in the directory) + $FolderFiles = + @(Get-ChildItem -Path $templateFolder.FullName -Recurse | + Where-Object { -not $_.PSIsContainer } | + ForEach-Object { + + # All FolderFile objects will have the following properties: + $fileInfo = $_ + $fileObject = [Ordered]@{ + Name = $fileInfo.Name #*Name (the name of the file) + Extension = $fileInfo.Extension #*Extension (the file extension) + Bytes = [IO.File]::ReadAllBytes($fileInfo.FullName)#*Bytes (the file content as a byte array) + Text = [IO.File]::ReadAllText($fileInfo.FullName)#*Text (the file content as text) + FullPath = $fileInfo.Fullname#*FullPath (the full path to the file) + } + if ($fileInfo.Extension -eq '.json') { + # If the file is JSON, two additional properties may be present: + #*Object (the file's text, converted from JSON) + $fileObject.Object = $fileObject.Text | ConvertFrom-Json + #*Schema (the value of the $schema property of the JSON object, if present) + $fileObject.schema = $fileObject.Object.'$schema' + } + $fileObject + }) + + if ($isMainTemplate) { # If the file was a main template, + # we set a few more variables: + #*MainTemplatePath (the path to the main template file) + $MainTemplatePath = "$TemplateFullPath" + #*MainTemplateText (the text of the main template file) + $MainTemplateText = [IO.File]::ReadAllText($MainTemplatePath) + #*MainTemplateObject (the main template, converted from JSON) + $MainTemplateObject = $MainTemplateText | ConvertFrom-Json + #*MainTemplateResources (the resources and child resources in the main template) + $MainTemplateResources = Expand-Resource -Resource $MainTemplateObject.resources + #*MainTemplateParameters (a hashtable of parameters in the main template) + $MainTemplateParameters = [Ordered]@{} + foreach ($prop in $MainTemplateObject.parameters.psobject.properties) { + $MainTemplateParameters[$prop.Name] = $prop.Value + } + #*MainTemplateVariables (a hashtable of variables in the main template) + $MainTemplateVariables = [Ordered]@{} + foreach ($prop in $MainTemplateObject.variables.psobject.properties) { + $MainTemplateVariables[$prop.Name] = $prop.Value + } + #*MainTemplateOutputs (a hashtable of outputs in the main template) + $MainTemplateOutputs = [Ordered]@{} + foreach ($prop in $MainTemplateObject.outputs.psobject.properties) { + $MainTemplateOutputs[$prop.Name] = $prop.Value + } + } + + # If we've found a CreateUIDefinition, we'll want to process it first. + if ($HasCreateUIDefinition) { + # Loop over the folder files and get every file that isn't createUIDefinition + $otherFolderFiles = @(foreach ($_ in $FolderFiles) { + if ($_.Name -ne 'CreateUIDefinition.json') { + $_ + } else { + $createUIDefFile = $_ + } + }) + # Then recreate the list with createUIDefinition that the front. + $FolderFiles = @(@($createUIDefFile) + @($otherFolderFiles) -ne $null) + } + + + $out = [Ordered]@{} + foreach ($v in $WellKnownVariables) { + $out[$v] = $ExecutionContext.SessionState.PSVariable.Get($v).Value + } + $out + } +} \ No newline at end of file diff --git a/test/template-tests/README.md b/test/template-tests/README.md new file mode 100644 index 000000000000..ae90127c50e4 --- /dev/null +++ b/test/template-tests/README.md @@ -0,0 +1,58 @@ +### Running Tests + +Test can be run directly in PowerShell, or run from the command line using a wrapper script. + +You can run the full suite of tests by using Test-AzureRMTemplate.cmd (on Windows) or Test-AzureRMTemplate.sh (on Linux), and passing in the path to a template. + +This will run the full suite of applicable tests on your template. To run a specific group of tests, use: + + + Test-AzureRMTemplate -TemplatePath $thePathToYourTemplate -Test deploymentTemplate + # This will run deployment template tests on all appropriate files + + Test-AzureRMTemplate -TemplatePath $thePathToYourTemplate -Test "Resources Should Have Location" + # This will run the specific test, 'Resources Should have Location', on all appropriate files + + Test-AzureRMTemplate -TemplatePath $thePathToYourTemplate -Test "Resources Should Have Location" -File MyNestedTemplate.json + # This will run the specific test, 'Resources Should have Location', but only on MyNestedTemplate.json + + +#### Running Tests on Linux + +Before you run the tests on Linux, you'll need to [install PowerShell Core](https://docs.microsoft.com/en-us/powershell/scripting/install/installing-powershell-core-on-linux?view=powershell-6). + +#### Running Tests in PowerShell + +To run the tests in PowerShell, you'll need to import the module. + + Import-Module .\AzRMTester.psd1 # assuming you're in the same directory as .\AzRMTester.psd1 + +You can then test a particular path by using: + + Test-AzureRMTemplate -TemplatePath /yourTemplatePath/ + +### Running Tests from the Command Line + +You can use a BASH file or Command Script to run on the command line. To do so, simply call Test-AzureRMTemplate.sh (or .cmd). This will pass the arguments down to the PowerShell script. To get help, pass a -? + + +### Inspecting Test Results + +By default, tests are run in Pester, which displays output in a colorized format, but does not return individual failures to the pipeline. +To inspect the results, use the -NoPester flag and assign the results to a variable +(you must be running in PowerShell): + + $TestResults = Test-AzureRMTemplate -TemplatePath /yourTemplatePath/ -NoPester + +To see failures, use Where-Object to filter the results + + $TestFailures = $TestResults | Where-Object { -not $_.Passed } + +Many test failures will return a TargetObject, for instance, the exact parameter within a template that had an issue. To extract out target objects from an error, use: + + $FailureTargetObjects = $TestFailures | + Select-Object -ExpandProperty Errors | + Select-Object -ExpandProperty TargetObject + + +Please note that not all test cases will return a target object. If no target object is returned, the target should be clear from the error message. \ No newline at end of file diff --git a/test/template-tests/Test-AzureRMTemplate.cmd b/test/template-tests/Test-AzureRMTemplate.cmd new file mode 100644 index 000000000000..c2e5e65e3f1c --- /dev/null +++ b/test/template-tests/Test-AzureRMTemplate.cmd @@ -0,0 +1 @@ +powershell.exe -noprofile -nologo -command "Import-Module Pester, '%~dp0AzRMTester.psd1'; Test-AzureRMTemplate %*; if ($error.Count) { exit 1}" diff --git a/test/template-tests/Test-AzureRMTemplate.ps1 b/test/template-tests/Test-AzureRMTemplate.ps1 new file mode 100644 index 000000000000..20b41c587764 --- /dev/null +++ b/test/template-tests/Test-AzureRMTemplate.ps1 @@ -0,0 +1,373 @@ +function Test-AzureRMTemplate +{ + <# + .Synopsis +Tests an Azure Resource Manager Template + .Description +Validates one or more Azure Resource Manager Templates. + .Notes +Test-AzureRMTemplate validates an Azure Resource Manager template using a number of small test scripts. + +Test scripts can be found in /testcases/GroupName, or provided with the -TestScript parameter. + +Each test script has access to a set of well-known variables: + +* TemplateFullPath (The full path to the template file) +* TemplateFileName (The name of the template file) +* TemplateText (The template text) +* TemplateObject (The template object) +* FolderName (The name of the directory containing the template file) +* FolderFiles (a hashtable of each file in the folder) +* IsMainTemplate (a boolean indicating if the template file name is mainTemplate.json) +* CreateUIDefintionFullPath (the full path to createUIDefintion.json) +* CreateUIDefinitionText (the text of createUIDefintion.json) +* CreateUIDefinitionObject ( the createUIDefintion object) +* HasCreateUIDefintion (a boolean indicating if the directory includes createUIDefintion.json) +* MainTemplateText (the text of the main template file) +* MainTemplateObject (the main template file, converted from JSON) +* MainTemplateResources (the resources and child resources of the main template) +* MainTemplateParameters (a hashtable containing the parameters found in the main template) +* MainTemplateVariables (a hashtable containing the variables found in the main template) +* MainTemplateOutputs (a hashtable containing the outputs found in the main template) + + #> + [CmdletBinding(DefaultParameterSetName='NearbyTemplate')] + param( + # The path to an Azure resource manager template + [Parameter(Mandatory=$true,Position=0,ValueFromPipelineByPropertyName=$true,ParameterSetName='SpecificTemplate')] + [Alias('Fullname','Path')] + [string] + $TemplatePath, + + # One or more test cases or groups. If this parameter is provided, only those test cases and groups will be run. + [Parameter(Position=1)] + [Alias('Tests')] + [string[]] + $Test, + + # If provided, will only validate files in the template directory matching one of these wildcards. + [Parameter(Position=2)] + [Alias('Files')] + [string[]] + $File, + + # A set of test cases. If not provided, the files in /testcases will be used as input. + [Parameter(ValueFromPipelineByPropertyName=$true)] + [ValidateScript({ + foreach ($k in $_.Keys) { + if ($k -isnot [string]) { + throw "All keys must be strings" + } + } + foreach ($v in $_.Values) { + if ($v -isnot [ScriptBlock] -and $v -isnot [string]) { + throw "All values must be script blocks or strings" + } + } + return $true + })] + [Alias('TestCases')] + [Collections.IDictionary] + $TestCase = [Ordered]@{}, + + # A set of test groups. Test groups will be automatically populated by the directory names in /testcases. + [Parameter(ValueFromPipelineByPropertyName=$true)] + [ValidateScript({ + foreach ($k in $_.Keys) { + if ($k -isnot [string]) { + throw "All keys must be strings" + } + } + foreach ($v in $_.Values) { + if ($v -isnot [string]) { + throw "All values must be strings" + } + } + return $true + })] + [Collections.IDictionary] + $TestGroup = [Ordered]@{}, + + # If set, will not run tests in Pester. + [switch] + $NoPester) + + begin { + # First off, let's get all of the built-in test scripts. + $testCaseSubdirectory = 'testcases' + $myLocation = $MyInvocation.MyCommand.ScriptBlock.File + $testScripts= @($myLocation| # To do that, we start from the current file, + Split-Path | # get the current directory, + Get-ChildItem -Filter $testCaseSubdirectory | # get the cases directory, + Get-ChildItem -Filter *.test.ps1 -Recurse) # and get all test.ps1 files within it. + + + $builtInTestCases = @{} + # Next we'll define some human-friendly built-in groups. + $builtInGroups = @{ + 'all' = 'deploymentTemplate', 'createUIDefinition' + 'mainTemplateTests' = 'deploymentTemplate' + } + + + # Now we loop over each potential test script + foreach ($testScript in $testScripts) { + # The test file name (minus .test.ps1) becomes the name of the test. + $TestName = $testScript.Name -ireplace '\.test\.ps1$', '' -replace '_', ' ' -replace '-', ' ' + $testDirName = $testScript.Directory.Name + if ($testDirName -ne $testCaseSubdirectory) { # If the test case was in a subdirectory + if (-not $builtInGroups.$testDirName) { + $builtInGroups.$testDirName = @() + } + # then the subdirectory name is the name of the test group. + $builtInGroups.$testDirName += $TestName + } else { + # If there was no subdirectory, put the test in a special group called "ungrouped". + if (-not $builtInGroups.Ungrouped) { + $builtInGroups.Ungrouped = @() + } + $builtInGroups.Ungrouped += $TestName + } + $builtInTestCases[$testName] = $testScript.Fullname + } + + # This lets our built-in groups be automatically defined by their file structure. + + + # Next we want to declare some internal functions: + #*Test-Case (executes a test, given a set of parameters) + function Test-Case($TheTest, $TestParameters = @{}) { + $testCommandParameters = + if ($TheTest -is [ScriptBlock]) { + $function:f = $TheTest + ([Management.Automation.CommandMetaData]$function:f).Parameters + Remove-Item function:f + } elseif ($TheTest -is [string]) { + $testCmd = $ExecutionContext.SessionState.InvokeCommand.GetCommand($TheTest, 'ExternalScript') + if (-not $testCmd) { return } + ([Management.Automation.CommandMetaData]$testCmd).Parameters + } else { + return + } + $testInput = @{} + $TestParameters + + foreach ($k in @($testInput.Keys)) { + if (-not $testCommandParameters.ContainsKey($k)) { + $testInput.Remove($k) + } + } + + if ($NoPester) { + & $TheTest @testInput 2>&1 3>&1 + } else { + & $TheTest @testInput + } + } + + #*Test-Group (executes a group of tests) + function Test-Group { + $testQueue = [Collections.Queue]::new(@($GroupName)) + while ($testQueue.Count) { + $dq = $testQueue.Dequeue() + if ($TestGroup.$dq) { + foreach ($_ in $TestGroup.$dq) { + $testQueue.Enqueue($_) + } + continue + } + + if ($ValidTestList -and $ValidTestList -notcontains $dq) { + continue + } + + if ($NoPester) { + $testStartedAt = [DateTime]::Now + $testCaseOutput = Test-Case $testCase.$dq $TestInput *>&1 + $testTook = [DateTime]::Now - $testStartedAt + + $testErrors = + @(foreach ($_ in $testCaseOutput) { + if ($_ -is [Exception] -or $_ -is [Management.Automation.ErrorRecord]) { $_ } + }) + + $testWarnings = + @(foreach ($_ in $testCaseOutput) { + if ($_ -is [Management.Automation.WarningRecord]) { $_ } + }) + + New-Object PSObject -Property ([Ordered]@{ + pstypename = 'Template.Validation.Test.Result' + Errors = $testErrors + Warnings = $testWarnings + Passed = $testErrors.Count -lt 1 + Group = $GroupName + Name = $dq + Timespan = $testTook + File = $fileInfo + }) + } else { + it $dq { + # Pester tests only fail on a terminating error, + $errorMessages = Test-Case $testCase.$dq $TestInput 2>&1 | + Where-Object { $_ -is [Management.Automation.ErrorRecord] } | + # so collect all non-terminating errors. + Select-Object -ExpandProperty Exception | + Select-Object -ExpandProperty Message + + if ($errorMessages) { # If any were found, + throw ($errorMessages -join ([Environment]::NewLine)) # throw. + } + } + } + } + } + + #*Test-FileList (tests a list of files) + function Test-FileList { + foreach ($fileInfo in $FolderFiles) { + $matchingGroups = + @(if ($fileInfo.Schema) { + foreach ($key in $TestGroup.Keys) { + if ("$key".StartsWith("_") -or "$key".StartsWith('.')) { continue } + if ($fileInfo.Schema -match $key) { + $key + } + } + } else { + foreach ($key in $TestGroup.Keys) { + if ($fileInfo.Extension -eq '.json' -and + ($fileInfo.Name -ireplace '\.test\.ps1', '') -match $key) { + $key; continue + } + if (-not ("$key".StartsWith('_') -or "$key".StartsWith('.'))) { continue } + if ($fileInfo.Extension -eq "$key".Replace('_', '.')) { + $key + } + } + }) + + if ($TestGroup.Ungrouped) { + $matchingGroups += 'Ungrouped' + } + + if (-not $matchingGroups) { continue } + if ($fileInfo.Schema -like '*deploymentTemplate*') { + $isMainTemplate = 'mainTemplate.json', 'azureDeploy.json' -contains $fileInfo.Name + $templateFileName = $fileInfo.Name + $TemplateObject = $fileInfo.Object + $TemplateText = $fileInfo.Text + } + foreach ($groupName in $matchingGroups) { + $testInput = @{} + foreach ($_ in $WellKnownVariables) { + $testInput[$_] = $ExecutionContext.SessionState.PSVariable.Get($_).Value + } + $ValidTestList = if ($test) { + @(Get-TestGroups $test -includeTest) + } else { + $null + } + if ($NoPester) { + $context = "$($fileInfo.Name)->$groupName" + Test-Group + } else { + context "$($fileInfo.Name)->$groupName" ${function:Test-Group} + + } + } + } + + } + + #*Get-TestGroups (expands nested test groups) + function Get-TestGroups([string[]]$GroupName, [switch]$includeTest) { + foreach ($_ in $GroupName) { + if ($TestGroup[$_]) { + Get-TestGroups $testGroup[$_] -includeTest:$includeTest + } elseif ($IncludeTest -and $TestCase[$_]) { + $_ + } + } + } + } + + process { + # If no template was passed, + if ($PSCmdlet.ParameterSetName -eq 'NearbyTemplate') { + # attempt to find one in the current directory and it's subdirectories + $possibleJsonFiles = @(Get-ChildItem -Filter *.json -Recurse | + Sort-Object Name -Descending | # (sort by name descending so that MainTemplate.json comes first). + Where-Object { + 'azureDeploy.json', 'mainTemplate.json' -contains $_.Name + }) + + + # If more than one template was found, warn which one we'll be testing. + if ($possibleJsonFiles.Count -gt 1) { + Write-Error "More than one potential template file found beneath '$pwd'. Please have only azureDeploy.json or mainTemplate.json, not both." + return + } + + + # If no potential files were found, write and error and return. + if (-not $possibleJsonFiles) { + Write-Error "No potential templates found beneath '$pwd'. Templates should be named azureDeploy.json or mainTemplate.json." + return + } + + + # If we could find a potential json file, recursively call yourself. + $possibleJsonFiles | + Select-Object -First 1 | + Test-AzureRMTemplate @PSBoundParameters + + return + } + + # First, merge the built-in groups and test cases with any supplied by the user. + foreach ($kv in $builtInGroups.GetEnumerator()) { + if (-not $testGroup[$kv.Key]) { + $TestGroup[$kv.Key] = $kv.Value + } + } + foreach ($kv in $builtInTestCases.GetEnumerator()) { + if (-not $testCase[$kv.Key]) { + $TestCase[$kv.Key]= $kv.Value + } + } + + $expandedTemplate =Expand-AzureRMTemplate -TemplatePath $templatePath + if (-not $expandedTemplate) { return } + $wellKnownVariables = $expandedTemplate.Keys + + foreach ($kv in $expandedTemplate.GetEnumerator()) { + $ExecutionContext.SessionState.PSVariable.Set($kv.Key, $kv.Value) + } + + # If a file list was provided, + if ($PSBoundParameters.File) { + $FolderFiles = @(foreach ($ff in $FolderFiles) { # filter the folder files. + $matched = @(foreach ($_ in $file) { + $ff.Name -like $_ # If file the name matched any of valid patterns. + }) + if ($matched -eq $true) + { + $ff # then we include it. + } + }) + } + + + + # Now that the filelist and test groups are set up, we use Test-FileList to test the list of files. + + + if ($NoPester) { # If we're not running Pester, + Test-FileList # we just call it directly. + } + else { + # If we're running Pester, we pass the function defintion as a parameter to describe. + describe "Validating Azure Template $TemplateName" ${function:Test-FileList} + } + } +} \ No newline at end of file diff --git a/test/template-tests/Test-AzureRMTemplate.sh b/test/template-tests/Test-AzureRMTemplate.sh new file mode 100644 index 000000000000..2a103995d359 --- /dev/null +++ b/test/template-tests/Test-AzureRMTemplate.sh @@ -0,0 +1,2 @@ +#!/bin/sh +powershell -noprofile -nologo -command "Import-Module Pester, '$(dirname $(readlink -f $0))/AzRMTester.psd1'; Test-AzureRMTemplate $@ ; if (\$error.Count) { exit 1}" \ No newline at end of file diff --git a/test/template-tests/testcases/CreateUIDefinition/CreateUIDefinition-Must-Have-Parameters.test.ps1 b/test/template-tests/testcases/CreateUIDefinition/CreateUIDefinition-Must-Have-Parameters.test.ps1 new file mode 100644 index 000000000000..eacde3d1c5b7 --- /dev/null +++ b/test/template-tests/testcases/CreateUIDefinition/CreateUIDefinition-Must-Have-Parameters.test.ps1 @@ -0,0 +1,15 @@ +param( +[Parameter(Mandatory=$true,Position=0)] +[PSObject] +$CreateUIDefinitionObject +) + +if (-not $CreateUIDefinitionObject.parameters) { + throw "CreateUIDefinition is missing a parameters property" +} + +if (-not $CreateUIDefinitionObject.parameters.basics) { + Write-Error "CreateUIDefinition is missing .parameters.basics" +} + + diff --git a/test/template-tests/testcases/CreateUIDefinition/CreateUIDefintion-Should-Have-Schema.test.ps1 b/test/template-tests/testcases/CreateUIDefinition/CreateUIDefintion-Should-Have-Schema.test.ps1 new file mode 100644 index 000000000000..39f1af371ee6 --- /dev/null +++ b/test/template-tests/testcases/CreateUIDefinition/CreateUIDefintion-Should-Have-Schema.test.ps1 @@ -0,0 +1,32 @@ +param( +[Parameter(Mandatory=$true,Position=0)] +[PSObject] +$CreateUIDefinitionObject +) + +if (-not $CreateUIDefinitionObject.'$Schema') { + throw "CreateUIDefinition is missing a `$schema property" +} + +if (-not $CreateUIDefinitionObject.'$schema' -cne 'https://schema.management.azure.com/schemas/0.1.2-preview/CreateUIDefinition.MultiVm.json#') { + throw "CreateUIDefintion has an incorrect schema. Schema should be https://schema.management.azure.com/schemas/0.1.2-preview/CreateUIDefinition.MultiVm.json#, and is '$($CreateUIDefinitionObject.'$schema')'" +} + +if (-not $CreateUIDefinitionObject.version) { + throw "CreateUIDefinition is missing a version" +} + +# Remove any preview chunk and cast the remaining portion as a version (make clearer) +$schemaVersion = $CreateUIDefinitionObject.'$Schema' -split '/' -ne '' | + ? { + $str = $_ + $firstPart, $rest = $_ -split '-' + $firstPart -as [Version] -gt '0.0' + } | + Select-Object -First 1 + +if ($CreateUIDefinitionObject.version -ne $schemaVersion) { + throw "CreateUIDefinition version ($($CreateUIDefinitionObject.version)) is different from schema version ($schemaVersion)" +} + + diff --git a/test/template-tests/testcases/CreateUIDefinition/Handler-Must-Be-Microsoft.Compute.MultiVM.test.ps1 b/test/template-tests/testcases/CreateUIDefinition/Handler-Must-Be-Microsoft.Compute.MultiVM.test.ps1 new file mode 100644 index 000000000000..262b3094a60a --- /dev/null +++ b/test/template-tests/testcases/CreateUIDefinition/Handler-Must-Be-Microsoft.Compute.MultiVM.test.ps1 @@ -0,0 +1,16 @@ +param( +[Parameter(Mandatory=$true,Position=0)] +[PSObject] +$CreateUIDefinitionObject +) + +if (-not $CreateUIDefinitionObject.handler) { + throw "CreateUIDefinition is missing handler property" +} + +if ($CreateUIDefinitionObject.handler -cne 'Microsoft.Compute.MultiVm') { + throw "The handler for CreateUIDefinition must be Microsoft.Compute.MultiVm" +} + + + diff --git a/test/template-tests/testcases/CreateUIDefinition/Location-Should-Be-In-Outputs.test.ps1 b/test/template-tests/testcases/CreateUIDefinition/Location-Should-Be-In-Outputs.test.ps1 new file mode 100644 index 000000000000..654f02508e75 --- /dev/null +++ b/test/template-tests/testcases/CreateUIDefinition/Location-Should-Be-In-Outputs.test.ps1 @@ -0,0 +1,17 @@ +param( +[Parameter(Mandatory=$true,Position=0)] +[PSObject] +$CreateUIDefinitionObject +) + +if (-not $CreateUIDefinitionObject.outputs.location) { + throw "Location is missing from outputs" +} + + +if ("$($CreateUIDefinitionObject.outputs.location)".Trim() -ne '[location()]') { + throw "CreateUIDefinition.outputs.location must be [location()]" +} + + + diff --git a/test/template-tests/testcases/CreateUIDefinition/Outputs-Must-Be-Present-In-Template-Parameters.test.ps1 b/test/template-tests/testcases/CreateUIDefinition/Outputs-Must-Be-Present-In-Template-Parameters.test.ps1 new file mode 100644 index 000000000000..e93085f0563e --- /dev/null +++ b/test/template-tests/testcases/CreateUIDefinition/Outputs-Must-Be-Present-In-Template-Parameters.test.ps1 @@ -0,0 +1,29 @@ +param( +[Parameter(Mandatory=$true,Position=0)] +[PSObject] +$CreateUIDefinitionObject, + +[Parameter(Mandatory=$true,Position=1)] +[PSObject] +$TemplateObject +) + + +if (-not $CreateUIDefinitionObject.parameters.outputs) { + throw "CreateUIDefinition is missing the .parameters.outputs property" +} + + +foreach ($output in $parameterInfo.outputs.psobject.properties) { + $outputName = $output.Name + if ($outputName -eq 'applicationresourcename') { + # subject to future removal, added by publishing process + continue + } + if (-not $TemplateObject.parameters.$outputName) { + Write-Error "output $outputName does not exist in template.parameters" + } +} + + + diff --git a/test/template-tests/testcases/CreateUIDefinition/Parameters-Without-Default-Must-Exist-In-CreateUIDefinition.test.ps1 b/test/template-tests/testcases/CreateUIDefinition/Parameters-Without-Default-Must-Exist-In-CreateUIDefinition.test.ps1 new file mode 100644 index 000000000000..3eb2ec011ba4 --- /dev/null +++ b/test/template-tests/testcases/CreateUIDefinition/Parameters-Without-Default-Must-Exist-In-CreateUIDefinition.test.ps1 @@ -0,0 +1,21 @@ +param( +[Parameter(Mandatory=$true)] +[PSObject] +$TemplateObject, + +[Parameter(Mandatory=$true)] +[PSObject] +$CreateUIDefinitionObject +) + +foreach ($parameter in $TemplateObject.parameters.psobject.properties) { + $parameterName = $parameter.Name + $parameterInfo = $parameter.Value + $defaultValue = $parameterInfo.defaultValue + if (-not $defaultValue) { # PowerShell -not will cover both null and blank strings + if (-not $CreateUIDefinitionObject.outputs.$parameterName) { + Write-Error "$parameterName does not have a default value, and is not defined in CreateUIDefinition.outuputs" -ErrorId Parameter.Without.Default.Missing.From.CreateUIDefinition -TargetObject $TemplateObject.parameters + continue + } + } +} \ No newline at end of file diff --git a/test/template-tests/testcases/CreateUIDefinition/Textboxes-Are-Well-Formed.test.ps1 b/test/template-tests/testcases/CreateUIDefinition/Textboxes-Are-Well-Formed.test.ps1 new file mode 100644 index 000000000000..8702f10cf369 --- /dev/null +++ b/test/template-tests/testcases/CreateUIDefinition/Textboxes-Are-Well-Formed.test.ps1 @@ -0,0 +1,44 @@ +param( +[Parameter(Mandatory=$true,Position=0)] +[PSObject] +$CreateUIDefinitionObject +) + + +function findTextBoxes { + param([Parameter(ValueFromPipelineByPropertyName=$true,Position=0)][PSObject]$value) + process { + if (-not $value) { return } + if ($value -is [string] -or $value -is [int] -or $value -is [bool] -or $value -is [double]) { + return + } + + if ($value.type -eq 'microsoft.common.textbox') { + return $value + } + if ($value -is [Object[]]) { + $value | + & $findTextBoxes -value { $_ } + } else { + $value.psobject.properties | + findTextBoxes + } + + } +} + +$allTextBoxes = findTextBoxes $CreateUIDefinitionObject +foreach ($textbox in $allTextBoxes) { + if (-not $textbox.constraints) { + Write-Error "Textbox $($textbox.Name) is missing constraints" + } else { + if (-not $textbox.constraints.regex) { + Write-Error "Textbox $($textbox.Name) is missing constraints.regex" + } + if (-not $textbox.constraints.validationMessage) { + Write-Error "Textbox $($textbox.Name) is missing constraints.validationMessage" + } + } +} + + diff --git a/test/template-tests/testcases/deploymentTemplate/Location-Should-Not-Be-Hardcoded.test.ps1 b/test/template-tests/testcases/deploymentTemplate/Location-Should-Not-Be-Hardcoded.test.ps1 new file mode 100644 index 000000000000..a73c0ed67378 --- /dev/null +++ b/test/template-tests/testcases/deploymentTemplate/Location-Should-Not-Be-Hardcoded.test.ps1 @@ -0,0 +1,27 @@ +param( +[Parameter(Mandatory=$true,Position=0)] +[string]$TemplateText, + +[Parameter(Mandatory=$true,Position=1)] +[string]$TemplateObject, + +[Parameter(Mandatory=$true,Position=1)] +[switch]$IsMainTemplate +) +$TemplateObjectCopy = $templateText | ConvertFrom-Json +$TemplateObjectCopy.parameters.psobject.properties.remove('location') +$TemplateWithoutLocationParameter = $TemplateObjectCopy | + ConvertTo-Json -Depth 10 + + +$locationParameter = $templateObject.parameters.location + +if ($locationParameter -and + $locationParameter.defaultvalue -ne '[resourceGroup().location]' -and + $IsMainTemplate) { + Write-Error "Location parameter must not be hardcoded. The default value should be [resourceGroup().location]." -ErrorId Location.Parameter.Hardcoded -TargetObject $parameter +} + +if ($TemplateWithoutLocationParameter -like '*resourceGroup().location*') { + Write-Error "$TemplateFileName must use the location parameter, not resourceGroup().location (except when used as a default value)" -ErrorId Location.Parameter.Should.Be.Used -TargetObject $parameter +} \ No newline at end of file diff --git a/test/template-tests/testcases/deploymentTemplate/ManagedIdentityExtension-must-not-be-used.test.ps1 b/test/template-tests/testcases/deploymentTemplate/ManagedIdentityExtension-must-not-be-used.test.ps1 new file mode 100644 index 000000000000..9fde8d5ed682 --- /dev/null +++ b/test/template-tests/testcases/deploymentTemplate/ManagedIdentityExtension-must-not-be-used.test.ps1 @@ -0,0 +1,11 @@ +param( +[Parameter(Mandatory=$true,Position=0)] +[string] +$TemplateObject +) + +$resourcesJson = $TemplateObject.resources | ConvertTo-Json -Depth 10 + +if ($resourcesJson -match 'ManagedIdentityExtension') { + Write-Error "Managed Identity Extension must not be used" -ErrorId ManagedIdentityExtension.Was.Used +} \ No newline at end of file diff --git a/test/template-tests/testcases/deploymentTemplate/Min-And-Max-Value-Are-Numbers.test.ps1 b/test/template-tests/testcases/deploymentTemplate/Min-And-Max-Value-Are-Numbers.test.ps1 new file mode 100644 index 000000000000..0e2d02888c88 --- /dev/null +++ b/test/template-tests/testcases/deploymentTemplate/Min-And-Max-Value-Are-Numbers.test.ps1 @@ -0,0 +1,34 @@ +param( +[Parameter(Mandatory=$true,Position=0)] +[PSObject] +$TemplateObject +) +foreach ($parameter in $templateObject.parameters) { + $Min = $null + $Max = $null + if ($parameter.psobject.properties.item('MaxValue')) { + if ($parameter.maxValue -isnot [int]) { + Write-Error "$($Parameter.Name) maxValue is not an [int] (it's a [$($parameter.maxValue.GetType())])" ` + -ErrorId Parameter.Max.Not.Int -TargetObject $parameter + } else { + $max = $parameter.maxValue + } + + } + if ($parameter.psobject.properties.item('MinValue')) { + if ($parameter.minValue -isnot [int]) { + Write-Error "$($Parameter.Name) minValue is not an [int] (it's a [$($parameter.minValue.GetType())])" ` + -ErrorId Parameter.Max.Not.Int -TargetObject $parameter + } else { + $min = $ParameterName.minValue + } + } + + if ($max -eq $null -and $min -ne $null){ + Write-Error "$($Parameter.Name) missing max value" -ErrorId Parameter.Missing.Max -TargetObject $parameter + } + + if ($max -ne $null -and $min -eq $null){ + Write-Error "$($Parameter.Name) missing min value" -ErrorId Parameter.Missing.Min -TargetObject $parameter + } +} \ No newline at end of file diff --git a/test/template-tests/testcases/deploymentTemplate/Parameters-Must-Be-Referenced.test.ps1 b/test/template-tests/testcases/deploymentTemplate/Parameters-Must-Be-Referenced.test.ps1 new file mode 100644 index 000000000000..2c264ee3795b --- /dev/null +++ b/test/template-tests/testcases/deploymentTemplate/Parameters-Must-Be-Referenced.test.ps1 @@ -0,0 +1,15 @@ +param( +[Parameter(Mandatory=$true,Position=0)] +[PSObject] +$TemplateObject +) +foreach ($parameter in $TemplateObject.parameters.psobject.properties) { + if ($TemplateText -notmatch "parameters\(['`"]$($Parameter.Name)['`"]\)") { + Write-Error -Message "Unreferenced parameter: $($Parameter.Name)" ` + -ErrorId Parameters.Must.Be.Referenced -TargetObject $parameter + } +} + + + + diff --git a/test/template-tests/testcases/deploymentTemplate/Parameters-Property-Must-Exist.test.ps1 b/test/template-tests/testcases/deploymentTemplate/Parameters-Property-Must-Exist.test.ps1 new file mode 100644 index 000000000000..889dcac308c1 --- /dev/null +++ b/test/template-tests/testcases/deploymentTemplate/Parameters-Property-Must-Exist.test.ps1 @@ -0,0 +1,9 @@ +param( +[Parameter(Mandatory=$true,Position=0)] +[PSObject] +$TemplateObject +) + +if (-not $TemplateObject.psobject.properties.item('parameters')) { + throw "Parameters property must exist in the template" +} \ No newline at end of file diff --git a/test/template-tests/testcases/deploymentTemplate/Resources-Should-Have-Location.test.ps1 b/test/template-tests/testcases/deploymentTemplate/Resources-Should-Have-Location.test.ps1 new file mode 100644 index 000000000000..8d02308ebd9d --- /dev/null +++ b/test/template-tests/testcases/deploymentTemplate/Resources-Should-Have-Location.test.ps1 @@ -0,0 +1,15 @@ +param( +[Parameter(Mandatory=$true,Position=0)] +[PSObject] +$MainTemplateResources +) +foreach ($mtr in $MainTemplateResources) { + foreach ($resource in @(@($mtr) + $mtr.ParentResources)) { + if ($resource.Location) { + $location = "$($resource.location)".Trim() + if ($location -notmatch '^\[.*\]$' -and $location -ne 'global') { + Write-Error "Resource $($resource.Name) Location must be an expression or 'global'" -TargetObject $resource + } + } + } +} \ No newline at end of file diff --git a/test/template-tests/testcases/deploymentTemplate/Secure-String-Parameters-Cannot-Have-Default.test.ps1 b/test/template-tests/testcases/deploymentTemplate/Secure-String-Parameters-Cannot-Have-Default.test.ps1 new file mode 100644 index 000000000000..f98361b2469b --- /dev/null +++ b/test/template-tests/testcases/deploymentTemplate/Secure-String-Parameters-Cannot-Have-Default.test.ps1 @@ -0,0 +1,19 @@ +param( +[Parameter(Mandatory=$true,Position=0)] +[PSObject] +$TemplateObject +) +foreach ($parameterProp in $templateObject.parameters.psobject.properties) { + $parameter = $parameterProp.Value + $name = $parameterProp.Name + if ($parameter.Type -eq 'securestring' -and + $parameter.defaultValue) # Will return true when defaultvalue is not null or blank (blank values are OK). + { + Write-Error -Message "Parameter $name is a SecureString, and must not have a default value." ` + -ErrorId SecureString.Must.Not.Have.Default -TargetObject $parameter + } +} + + + + diff --git a/test/template-tests/testcases/deploymentTemplate/Virtual-Machines-Should-Not-Be-Preview.test.ps1 b/test/template-tests/testcases/deploymentTemplate/Virtual-Machines-Should-Not-Be-Preview.test.ps1 new file mode 100644 index 000000000000..ad0f5563a5a6 --- /dev/null +++ b/test/template-tests/testcases/deploymentTemplate/Virtual-Machines-Should-Not-Be-Preview.test.ps1 @@ -0,0 +1,25 @@ +param( +[Parameter(Mandatory=$true)] +[PSObject] +$TemplateObject +) +foreach ($resource in $templateObject.resources) { + # This is a PowerShell trick to simplify multiple -ors + # -notcontains checks that a list (on the left side) doesn't contain a value (on the right side) + # So this test will ignore resources that aren't /virtualmachines or /virtualmachineassets + if ('microsoft.compute/virtualmachinescalesets', + 'microsoft.compute/virtualmachines' -notcontains $resource.ResourceType) { + continue + } + $imageReference = $resource.virtualmachineprofile.storageprofile.imagereference + if (-not $imageReference) { + Write-Error "Virtual machine resource $($resource.Name) has no image to reference" -TargetObject $resource -ErrorId VM.Missing.Image + } + + if ($imageReference -like '*-preview') { + Write-Error "Virtual machine resource $($resource.Name) must not use a preview image" -TargetObject $ResourceType -ErrorId VM.Using.Preview.Image + } +} + + +