diff --git a/1-contribution-guide/README.md b/1-contribution-guide/README.md new file mode 100644 index 0000000..947ef9e --- /dev/null +++ b/1-contribution-guide/README.md @@ -0,0 +1,44 @@ +# Azure Managed Application samples + +This repository contains all currently available Azure Managed Application samples contributed by the community. The following information is relevant to get started with contributing to this repository. + ++ [**Contribution guide**](/1-contribution-guide/README.md#contribution-guide). Describes the minimal guidelines for contributing. ++ [**Best practices**](/1-contribution-guide/best-practices.md#best-practices). Best practices for improving the quality of Azure Managed Application design. ++ [**Git tutorial**](/1-contribution-guide/git-tutorial.md#git-tutorial). Step by step to get you started with Git. ++ [**Useful Tools**](/1-contribution-guide/useful-tools.md#useful-tools). Useful resources and tools for Azure development. + +## Contribution guide + +To make sure your Managed Application sample is added to this repository, please follow these guidelines. Any Managed Application sample that are out of compliance will be added to the **blacklist** and not be merged. + +## Files, folders and naming conventions + +1. Every Managed Application sample its associated files must be contained in its own **folder**. Name this folder so it represents what your Managed Application does. Usually this naming pattern looks like **appName-osName** or **level-platformCapability** (e.g. 101-managed-storage) + + **Required** - Numbering should start at 101. 100 is reserved for things that need to be at the top. + + **Protip** - Try to keep the name of your template folder short so that it fits inside the Github folder name column width. +2. Github uses ASCII for ordering files and folder. For consistent ordering **create all files and folders in lowercase**. The only **exception** to this guideline is the **README.md**, that should be in the format **UPPERCASE.lowercase**. +3. Include a **README.md** file that explains how the Managed Application works, and how to deploy. + + Guidelines on the README.md file below. +4. A Managed Application needs to include the following files: + + **mainTemplate.json** - The Resource Manager template that will deploy resources (and nested templates) + + **createUiDefinition.json** - The user interface definition file, to generate input parameters to the customer facing template in the [Azure portal](https://portal.azure.com) + + A generalized .zip file with all the artifacts for the Managed Application. +5. The custom scripts that are needed for successful template execution must be placed in a sub-folder called **scripts**. +6. Linked templates must be placed in a sub-folder called **nestedtemplates**. +7. Images used in the README.md must be placed in a folder called **images**. +8. an *azuredeploy.json* template which will create and initialize the Managed Application offering directly in the Azure subscription, referencing the artifacts in the reposotiry. + +![alt text](./images/structure.png "Files, folders and naming conventions") + +## README.md + +The README.md describes your deployment. A good description helps other community members to understand your deployment. The README.md uses [Github Flavored Markdown](https://guides.github.com/features/mastering-markdown/) for formatting text. If you want to add images to your README.md file, store the images in the **images** folder. Reference the images in the README.md with a relative path (e.g. `![alt text](images/namingConvention.png "Files, folders and naming conventions")`). This ensures the link will reference the target repository if the source repository is forked. A good README.md contains the following sections + ++ Deployment instructions ++ AzureDeploy/PowerShell/CLI example of automated import ++ Description of what the Managed Application will deploy ++ *Optional: Prerequisites ++ *Optional: Description on how to use the Managed Application ++ *Optional: Notes + +This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. diff --git a/1-contribution-guide/best-practices.md b/1-contribution-guide/best-practices.md new file mode 100644 index 0000000..6b25397 --- /dev/null +++ b/1-contribution-guide/best-practices.md @@ -0,0 +1,205 @@ +# Best practices + +## In general.. + ++ It is a good practice to pass your Managed Application templates and UiDefinition through a JSON linter to remove extraneous commas, parenthesis, brackets that may break the deployment. Try http://jsonlint.com/ or a linter package for your favorite editing environment (Visual Studio Code, Atom, Sublime Text, Visual Studio etc.) ++ It's also a good idea to format your JSON for better readability. You can use a JSON formatter package for your local editor or [format online using this link](https://www.bing.com/search?q=json+formatter). + +## The following guidelines are relevant to the Managed Application Resource Manager templates. + +* Template parameters should follow **camelCasing** + +Example: +````json + "parameters": { + "storagePrefixName": { + "type": "string", + "metadata": { + "description": "Specify the prefix of the storage account name" + } + } + } +```` + +* Minimize parameters whenever possible, this allows for a good "hello world" experience where the user doesn't have to answer a number of questions to complete a deployment. If you can use a variable or a literal, do so. + +* Only provide parameters for: + + * Things that are globally unique (e.g. website name). These are usually the endpoints the user will interact with post deployment, and need to be aware of. However, in many cases a unique name can be generated automatically by using the [uniqueString()](https://azure.microsoft.com/en-us/documentation/articles/resource-group-template-functions/#uniquestring) template language function. + * Other things a user must know to complete a workflow (e.g. admin user name on a VM) + * Secrets (e.g. admin password on a VM) + * Every template **must** include a parameter that specifies the location of the resources, and the *defaultValue* should be *resourceGroup().location* + * If you must include a parameter, define a defaultValue, unless the parameter is used for a passwords, storage account name prefix, or domain name label + +* Every parameter in the template should have the **lower-case description** tag specified using the metadata property. This looks like below + +````json + "parameters": { + "storageAccountType": { + "type": "string", + "metadata": { + "description": "The type of the new storage account created to store the VM disks" + } + } + } +```` + +* Template parameters **must not** include *allowedValues* for the following parameter types + * Vm Size + * Location + + +>**Note**:Use *createUiDefinition.json* for this purpose + +* When nested templates or scripts are being used, the *mainTemplate.json* **must** include a variable with the uri() function with deployment().properties.templateLink.uri - to automatically resolve the URL for nested templates and scripts. The variable(s) would look similar to this: + +````json + "variables": { + "nestedTemplateUrl": "[uri(deployment().properties.templateLink.uri, 'nestedtemplates/mytemplate.json')]", + "scriptsUrl": "[uri(deployment().properties.templateLink.uri, 'scripts/myscript.ps1')]" + } +```` + +* Template parameters **must not** include default values for parameters that represents the following types + * Storage Account Name prefix + * Domain Name Label + +>**Note**: Use *createUiDefinition.json* for this purpose, to avoid conflict + +* Do not create a parameter for a **storage account name**, but specify it is for **storage account name prefix**. Storage account names need to be lower case and can't contain hyphens (-) in addition to other domain name restrictions. A storage account has a limit of 24 characters. They also need to be globally unique. To prevent any validation issue configure a variables (using the expression **uniqueString** and a static value **storage**). Storage accounts with a common prefix (uniqueString) will not get clustered on the same racks. + +Example: + +````json + "parameters": { + "storageAccountNamePrefix": { + "type": "string", + "metadata": { + "description": "Prefix for the storage account name" + } + } + }, + "variables": { + "storageAccountName": "[concat(parameters('storageAccountNamePrefix'), uniqueString('storage'))]" + }, +```` + + +* Passwords **must** be passed into parameters of type **securestring**. Do not specify a defaultValue for a parameter that is used for a password or an SSH key. Passwords must also be passed to **customScriptExtension** using the **commandToExecute** property in protectedSettings. + +````json + "properties": { + "publisher": "Microsoft.Azure.Extensions", + "type": "CustomScript", + "version": "2.0", + "autoUpgradeMinorVersion": true, + "settings": { + "fileUris": [ + "[concat(variables('template').assets, '/lamp-app/install_lamp.sh')]" + ] + }, + "protectedSettings": { + "commandToExecute": "[concat('sh install_lamp.sh ', parameters('mySqlPassword'))]" + } + } +```` + +>**Note**: In order to ensure that secrets which are passed as parameters to virtualMachines/extensions are encrypted, the protectedSettings property of the relevant extensions must be used. + +* Using tags to add metadata to resources allows you to add additional information about your resources. A good use case for tags is adding metadata to a resource for billing detail purposes. + +* You can group variables into complex objects. You can reference a value from a complex object in the format variable.subentry (e.g. `"[variables('storage').storageAccounts.type]"`). Grouping variables helps you keep track of related variables and improves readability of the template. + +>**Note**: A complex object cannot contain an expression that references a value from a complex object. Define a separate variable for this purpose. + +* If you include Azure management services to your Managed Application, such as Log Analytics, Azure Automation, Backup and Site Recovery, you **must** **not** use additional parameters for these resource locations. Instead, use the following pattern using variables, to place those services in the closest available Azure region to the Resource Group + +````json + "logAnalyticsLocationMap": { + "eastasia": "southeastasia", + "southeastasia": "southeastasia", + "centralus": "westcentralus", + "eastus": "eastus", + "eastus2": "eastus", + "westus": "westcentralus", + "northcentralus": "westcentralus", + "southcentralus": "westcentralus", + "northeurope": "westeurope", + "westeurope": "westeurope", + "japanwest": "southeastasia", + "japaneast": "southeastasia", + "brazilsouth": "eastus", + "australiaeast": "australiasoutheast", + "australiasoutheast": "australiasoutheast", + "southindia": "southeastasia", + "centralindia": "southeastasia", + "westindia": "southeastasia", + "canadacentral": "eastus", + "canadaeast": "eastus", + "uksouth": "westeurope", + "ukwest": "westeurope", + "westcentralus": "westcentralus", + "westus2": "westcentralus", + "koreacentral": "southeastasia", + "koreasouth": "southeastasia", + "eastus2euap": "eastus" + }, + "logAnalyticsLocation": "[variables('logAnalyticsLocationMap')[parameters('location')]]" +```` + +>**NOTE**: To find the available Azure regions for a Resource Provider, you can use the following PowerShell cmdlet: +>```Get-AzureRmResourceProvider -ProviderNamespace Microsoft.OperationalInsights | select -ExpandProperty Locations``` + +The domainNameLabel property for publicIPAddresses **must** be **unique**. domainNameLabel is required to be between 3 and 63 characters long and to follow the rules specified by this regular expression ^[a-z][a-z0-9-]{1,61}[a-z0-9]$. As the uniqueString function will generate a string that is 13 characters long in the example below it is presumed that the dnsPrefixString prefix string has been checked to be no more than 50 characters long and to conform to those rules. + +>**Note**: The recommended approach for creating a publicIPAddresses is to use the Microsoft.Network.PublicIpAddressCombo in createUIDefinition.json which will validate the input and make sure the domainNameLabel is available, however if a Managed Application creates new publicIPAddresses in a template without using this element to provide parameters then it should ensure that the domainNameLabel properties used for them are unique + +````json + "parameters": { + "dnsPrefixString": { + "type": "string", + "maxLength": 50, + "metadata": { + "description": "DNS Label for the Public IP. Must be lowercase. It should match with the following regular expression: ^[a-z][a-z0-9-]{1,61}[a-z0-9]$ or it will raise an error." + } + } + }, + "variables": { + "dnsPrefix": "[concat(parameters('dnsPrefixString'),uniquestring(resourceGroup().id))]" + } +```` + +* For the public endpoints the user will interact with, you **must** provide this information in the **output** section in the templates, so it can be easily retrieved post deployment + +````json + "outputs": { + "vmEndpoint": { + "type": "string", + "value": "[reference(concat(parameters('vmName'), 'IP')).dnsSettings.fqdn]" + } + } +```` + +* If using *nested templates*, ensure you are referencing the outputs from the nested templates into the *mainTemplate.json* + +````json + "outputs": { + "vmEndpoint": { + "type": "string", + "value": "[reference('nestedDeployment').outputs.vmEndpoint.value]" + } + } +```` + +* To set the managed application resource name, you must use ````"applicationResourceName"```` in the ````createUiDefinition.json```` file. If not, the application will automatically get a GUID for this resource. +Example usage: + +````json + "outputs": { + "vmName": "[steps('appSettings').vmName]", + "trialOrProduction": "[steps('appSettings').trialOrProd]", + "userName": "[steps('vmCredentials').adminUsername]", + "pwd": "[steps('vmCredentials').vmPwd.password]", + "applicationResourceName": "[steps('appSettings').vmName]" + } +```` diff --git a/1-contribution-guide/images/artifacts.png b/1-contribution-guide/images/artifacts.png new file mode 100644 index 0000000..659c3cb Binary files /dev/null and b/1-contribution-guide/images/artifacts.png differ diff --git a/1-contribution-guide/images/downloadrepo.png b/1-contribution-guide/images/downloadrepo.png new file mode 100644 index 0000000..64a7589 Binary files /dev/null and b/1-contribution-guide/images/downloadrepo.png differ diff --git a/1-contribution-guide/images/managedapps.png b/1-contribution-guide/images/managedapps.png new file mode 100644 index 0000000..6b57213 Binary files /dev/null and b/1-contribution-guide/images/managedapps.png differ diff --git a/1-contribution-guide/images/structure.png b/1-contribution-guide/images/structure.png new file mode 100644 index 0000000..b77b2f6 Binary files /dev/null and b/1-contribution-guide/images/structure.png differ diff --git a/1-contribution-guide/images/validation.png b/1-contribution-guide/images/validation.png new file mode 100644 index 0000000..db2a3b1 Binary files /dev/null and b/1-contribution-guide/images/validation.png differ diff --git a/1-contribution-guide/psscript.md b/1-contribution-guide/psscript.md new file mode 100644 index 0000000..1a0b1d8 --- /dev/null +++ b/1-contribution-guide/psscript.md @@ -0,0 +1,152 @@ +# PowerShell sample to initialize Managed Application in Service Catalog + +1. Navigate to the folder where you extracted the sample you want to use +2. Use the following script to auto generate the *mainTemplate.json* to have the *"applianceDefinitionId"* property to match your environment. +**Ensure you change the parameters with your own values before you proceed.** +```powershell +[cmdletbinding()] +param( + [Parameter()] + [ValidateNotNullOrEmpty()] + [string] $location = 'westcentralus', + + [Parameter()] + [ValidateNotNullOrEmpty()] + [string] $rgName = 'knappliancedef', + + [Parameter()] + [ValidateNotNullOrEmpty()] + [string] $ManagedApplicationName = 'ManagedServiceFabric', + + [Parameter()] + [ValidateNotNullOrEmpty()] + [string] $ManagedApplicationDisplayName = 'Managed Service Fabric', + + [Parameter()] + [ValidateNotNullOrEmpty()] + [string] $ManagedApplicationDescription = 'Managed Service Fabric with Azure management', + + [Parameter()] + [ValidateNotNullOrEmpty()] + [string] $principalId = '78343385-2886-470d-a12a-dd31f8758617', + + [Parameter()] + [ValidateNotNullOrEmpty()] + [string] $roleDefinitionId = '8e3af657-a8ff-443c-a75c-2fe8c4bcb635', + + [Parameter()] + [ValidateNotNullOrEmpty()] + [string] $zipFilename = 'servicefabric.zip', + + [Parameter()] + [ValidateNotNullOrEmpty()] + [string] $storageAccountName = 'mystorageaccount', + + [Parameter()] + [ValidateNotNullOrEmpty()] + [string] $storageContainerName = 'container1' +) + +$localpath = if ($env:Build_Repository_LocalPath) { + $env:Build_Repository_LocalPath +} else { + '.' +} + +try { + # Set the mainTemplate resource Id to match parameters + # Add the parameters from the applianceMainTemplate.json to the mainTemplate.json + # Set the name of the resource to match the name of the Managed Application + + $applianceMainTemplate = (Get-Content -path ${LocalPath}\applianceMainTemplate.json) -join "`n" | ConvertFrom-Json + + $azuredeployjson = @' + { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": {}, + "variables": { + "applianceInfraResourceGroupId": "[concat(resourceGroup().id,'-application-resources')]" + }, + "resources": [ + { + "type": "Microsoft.Solutions/appliances", + "name": "", + "apiVersion": "2016-09-01-preview", + "location": "[resourceGroup().Location]", + "kind": "ServiceCatalog", + "properties": { + "managedResourceGroupId": "[variables('applianceInfraResourceGroupId')]", + "applianceDefinitionId": "appDefId", + "parameters": {} + } + } + ], + "outputs": {} + } +'@ + + $azuredeploytemplate = $azuredeployjson | ConvertFrom-Json + $azuredeploytemplate.parameters = $applianceMainTemplate.parameters + $applianceparameters = @{} + foreach ($parameter in $applianceMainTemplate.parameters.psobject.properties) { + $applianceparameter = @{'value' = "[parameters('$($parameter.Name)')]"} + $value = New-Object PSObject -Property $applianceparameter + $applianceparameters.Add($($parameter.Name), $value) + } + $azuredeploytemplate.resources[0].properties.parameters = New-Object PSObject -Property $applianceparameters + $ResourceId = "[concat('/subscriptions/',subscription().subscriptionId,'/resourceGroups/${rgName}/providers/Microsoft.Solutions/applianceDefinitions/${ManagedApplicationName}')]" + $azuredeploytemplate.resources[0].properties.applianceDefinitionId = $ResourceId + $azuredeploytemplate.resources[0].name = $ManagedApplicationName + + Write-Output "Creating mainTemplate with new applianceDefinitionId $ResourceId" + (ConvertTo-Json $azuredeploytemplate -Depth 10 ).Replace('\u0027', '''')|Out-File -FilePath ${LocalPath}\mainTemplate.json -Force -Encoding UTF8 + + Write-Output "Creating Zip File" + Compress-Archive -Path ${localPath}\applianceCreateUiDefinition.json, ${localPath}\mainTemplate.json, ${localPath}\applianceMainTemplate.json, ${localPath}\nestedtemplates -DestinationPath ${localPath}\${zipFilename} -Force + + Write-Output "Uploading Zip File" + $storageResource = Find-AzureRmResource -ResourceType Microsoft.Storage/storageAccounts -ResourceNameEquals $storageAccountName + $storageAccount = Get-AzureRmStorageAccount -StorageAccountName $storageAccountName -ResourceGroupName $storageResource.ResourceGroupName + $container = Get-AzureStorageContainer -Name $storageContainerName -MaxCount 1 -Context $storageAccount.Context -ErrorAction SilentlyContinue + + if ($container) { + if ($container.PublicAccess -eq 'off') { + throw 'Container must allow public access to blobs' + } + } else { + New-AzureStorageContainer -Name $StorageContainerName -Permission Blob -Context $storageAccount.Context + } + + $null = Set-AzureStorageBlobContent -Container $storageContainerName -File $zipFilename -Context $storageAccount.Context -Force + $packageFileUri = "https://${storageAccountName}.blob.core.windows.net/${storageContainerName}/${zipFilename}" + + $propertyObject = @{ + "LockLevel" = "ReadOnly" + "DisplayName" = $ManagedApplicationDisplayName + "Description" = $ManagedApplicationDescription + "Authorizations" = @( + @{ + "principalId" = $principalId + "roleDefinitionId" = $roleDefinitionId + } + ) + "PackageFileUri" = $packageFileUri + } + + Write-Output "Creating Application Definition" + $rg = Get-AzureRmResourceGroup -Name $rgname -location $location -ErrorAction SilentlyContinue + if (-not $rg) { + Write-Output "Creating Resource Group -Name $rgname -location $location" + New-AzureRmResourceGroup -Name $rgname -location $location + } + + Write-Output "Creating or Updating the Appliance Definiton" + New-AzureRmResource -ResourceName $ManagedApplicationName -ResourceType 'Microsoft.Solutions/applianceDefinitions' -ResourceGroupName $rgName -Location $location -ApiVersion '2016-09-01-preview' -PropertyObject $propertyObject -force +} catch { + throw $_ +} +``` +Open [Azure portal](https://portal.azure.com), Managed Application, Add, and you should see your newly created Managed Application offering + +![media](./images/managedapps.png) diff --git a/1-contribution-guide/useful-tools.md b/1-contribution-guide/useful-tools.md new file mode 100644 index 0000000..a0d964e --- /dev/null +++ b/1-contribution-guide/useful-tools.md @@ -0,0 +1,7 @@ +Learn more about [Creating and deploying Azure resource groups through Visual Studio](https://azure.microsoft.com/en-us/documentation/articles/vs-azure-tools-resource-groups-deployment-projects-create-deploy) + +Learn more about [Resource manager for VS Code](https://azure.microsoft.com/en-us/documentation/articles/resource-manager-vs-code/) + +Learn more about [Azure SDK and Tools for Visual Studio](https://www.visualstudio.com/features/azure-tools-vs) + +Learn more about all the Azure tools: https://azure.microsoft.com/en-us/tools/ \ No newline at end of file diff --git a/Azure CLI Samples/GetApplicationDefinitions/GetApplicationDefinition.sh b/Azure CLI Samples/GetApplicationDefinitions/GetApplicationDefinition.sh new file mode 100644 index 0000000..a050b43 --- /dev/null +++ b/Azure CLI Samples/GetApplicationDefinitions/GetApplicationDefinition.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +#counter for iterating through definitions +counter=0 + +#Get all applicationDefinitions names at subscription scope +for scdn in $(az resource list --resource-type "Microsoft.Solutions/applicationDefinitions" --output tsv --query [].name); do + +#get resource group of the definition +scdrg=$(az resource list --resource-type "Microsoft.Solutions/applicationDefinitions" --output tsv --query [$counter].resourceGroup) + +#get the path to the mainTemplate file +uriPath=$(az managedapp definition show --name $scdn --resource-group $scdrg --query artifacts[0].uri) + +#output the path to the Service Catalog Definition +az managedapp definition show --name $scdn --resource-group $scdrg --query id + +#output the uri to the ARM template the service catalog definition contains +echo "${uriPath/applicationResourceTemplate/mainTemplate}" +echo +let counter+=1 +done \ No newline at end of file diff --git a/Azure CLI Samples/GetApplicationDefinitions/readme.md b/Azure CLI Samples/GetApplicationDefinitions/readme.md new file mode 100644 index 0000000..96b879b --- /dev/null +++ b/Azure CLI Samples/GetApplicationDefinitions/readme.md @@ -0,0 +1 @@ +This Azure CLI sample returns all Azure Service Catalog Definition paths and their mainTemplate URIs in your given subscription diff --git a/Azure CLI Samples/readme.md b/Azure CLI Samples/readme.md new file mode 100644 index 0000000..790b058 --- /dev/null +++ b/Azure CLI Samples/readme.md @@ -0,0 +1 @@ +This folder contains different Azure CLI samples for Azure Managed Applications for your reference. diff --git a/Managed Application Sample Packages/101-managed-storage-account/azuredeploy.json b/Managed Application Sample Packages/101-managed-storage-account/azuredeploy.json new file mode 100644 index 0000000..470955b --- /dev/null +++ b/Managed Application Sample Packages/101-managed-storage-account/azuredeploy.json @@ -0,0 +1,82 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "type": "string", + "defaultValue": "ManagedWebApp", + "metadata": { + "description": "Provide a name for the managed application" + } + }, + "location": { + "type": "string", + "metadata": { + "description": "Specify the Azure region to place the application definition" + } + }, + "lockLevel": { + "type": "string", + "allowedValues": [ + "ReadOnly", + "CanNotDelete" + ], + "defaultValue": "ReadOnly", + "metadata": { + "description": "Specify the resource lock being used for the managed application" + } + }, + "authorizations": { + "type": "array", + "metadata": { + "description": "Provide the authorization mapping for the managed application." + } + }, + "description": { + "type": "string", + "defaultValue": "Managed Azure IaaS Web Application", + "metadata": { + "description": "Provide a brief description of the managed application" + } + }, + "displayName": { + "type": "string", + "defaultValue": "Managed Azure Web Application", + "metadata": { + "description": "Display name for the managed application" + } + } + }, + "variables": { + "packageFileUri": "[uri(deployment().properties.templateLink.uri, 'managed-storage-account.zip')]" + }, + "resources": [ + { + "apiVersion": "2019-07-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "type": "Microsoft.Solutions/applicationDefinitions", + "properties": { + "lockLevel": "[parameters('lockLevel')]", + "authorizations": "[array(parameters('authorizations'))]", + "description": "[parameters('description')]", + "displayName": "[parameters('displayName')]", + "packageFileUri": "[variables('packageFileUri')]" + } + } + ], + "outputs": { + "managedApplicationName": { + "type": "string", + "value": "[parameters('name')]" + }, + "lockLevel": { + "type": "string", + "value": "[parameters('locklevel')]" + }, + "packageFileUri": { + "type": "string", + "value": "[variables('packageFileUri')]" + } + } +} \ No newline at end of file diff --git a/Managed Application Sample Packages/101-managed-storage-account/createUiDefinition.json b/Managed Application Sample Packages/101-managed-storage-account/createUiDefinition.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/Managed Application Sample Packages/101-managed-storage-account/createUiDefinition.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/Managed Application Sample Packages/101-managed-storage-account/mainTemplate.json b/Managed Application Sample Packages/101-managed-storage-account/mainTemplate.json new file mode 100644 index 0000000..aa6f34c --- /dev/null +++ b/Managed Application Sample Packages/101-managed-storage-account/mainTemplate.json @@ -0,0 +1,23 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "storageAccountName": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.Storage/storageAccounts", + "name": "[parameters('storageAccountName')]", + "apiVersion": "2019-06-01", + "location": "[resourceGroup().location]", + "sku": { + "name": "Standard_LRS" + }, + "kind": "Storage", + "properties": {} + } + ], + "outputs": {} +} \ No newline at end of file diff --git a/Managed Application Sample Packages/101-managed-storage-account/managed-storage-account.zip b/Managed Application Sample Packages/101-managed-storage-account/managed-storage-account.zip new file mode 100644 index 0000000..58aa27d Binary files /dev/null and b/Managed Application Sample Packages/101-managed-storage-account/managed-storage-account.zip differ diff --git a/Managed Application Sample Packages/101-managed-storage-account/readme.md b/Managed Application Sample Packages/101-managed-storage-account/readme.md new file mode 100644 index 0000000..8311fa5 --- /dev/null +++ b/Managed Application Sample Packages/101-managed-storage-account/readme.md @@ -0,0 +1,42 @@ +# Managed Azure Storage Account (without Ui Definition) + +>Note: This sample is for Managed Application in Service Catalog. For Marketplace, please see these instructions: +[**Marketplace Managed Application**](/1-contribution-guide/marketplace.md#transitioning-to-marketplace) + +## How to try out this Azure Managed Application + +Step 1: Create an ARM template (Use the applianceMainTemplate.json) + +Step 2: Create a ManagedApp template (User the mainTemplate.json) + +Step 3: Create create ui definition file (Use applianceCreateUiDefinition.json) + +Step 4: Zip the files above (Use managed_singlestorageaccount.zip) and upload to public blob storage or upload to public github accout and get the URL. + + e.g. if you have upload to azure storage blob it would look something like https://.blob.core.windows.net//managed_singlestorageaccount.zip) + Please make sure that you copy paste this URL in browser and see if you see zip file is being downloaded. If this doesn't happen change container's access to "public ReadOnly" + +Step 5: Create ManagedApp Definition + +1. Get principal id: + + Create user group (From Azure Portal Go to "Azure Active Directory" -> "All groups" -> "New group" ->Create a new group and add other than your azure account in the group) + Copy paste "Object Id" of the user group and use it as principal id + +2. For the demo use roleDefinitionId as 8e3af657-a8ff-443c-a75c-2fe8c4bcb635 (This is built in role definition for the "Owner") + +3. Go to Azure Portal (portal.azure.com) -> from the top right corner select "Cloud Shell" and run following commmand: + + az managedapp definition create --display-name HelloManagedAppDef --description "A simple managedApp definition consist of a storage account" -a ":8e3af657-a8ff-443c-a75c-2fe8c4bcb635" -l westcentralus --lock-level ReadOnly -g appdefRG -n helloManagedAppDef --package-file-uri https://.blob.core.windows.net//managed_singlestorageaccount.zip + +Step 6: Create ManagedApp + +Create managed app using managed app definition created above + + az managedapp create --location westcentralus --kind serviceCatalog --managed-rg-id /subscriptions//resourceGroups/helloManagedByRG --name helloworldmanagedApp --resource-group helloManagedApp --managedapp-definition-id "/subscriptions//resourceGroups/appdefRG/providers/Microsoft.Solutions/applianceDefinitions/helloManagedAppDef" + +After this you should be able to see two resource group appdefRG and helloManagedByRG. +appdefRG should have helloManagedAppDef resource +helloManagedByRG resource group should have a storage account + +Now if you have selected other than your storage account in the user group you won't be able to delete or modify any thing on the helloManagedByRG and the storage account underneath. User part of the principal id (user group) an even do all the operation on the storage account \ No newline at end of file diff --git a/Managed Application Sample Packages/101-managed-vm/README.md b/Managed Application Sample Packages/101-managed-vm/README.md new file mode 100644 index 0000000..57c50f0 --- /dev/null +++ b/Managed Application Sample Packages/101-managed-vm/README.md @@ -0,0 +1,50 @@ +# Managed Virtual Machine + +>Note: This sample is for Managed Application in Service Catalog. For Marketplace, please see these instructions: +[**Marketplace Managed Application**](https://docs.microsoft.com/en-us/azure/managed-applications/publish-marketplace-app) + +## Deploy this sample to your Service Catalog + +### Deploy using Azure Portal + +Clicking on the button below, will create the Managed Application definition to a Resource Group in your Azure subscription. + +[![Deploy to Azure](http://azuredeploy.net/deploybutton.png)](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2Fazure%2Fazure-managedapp-samples%2Fmaster%2FManaged%2520Application%2520Sample%2520Packages%2F101-managed-vm%2Fazuredeploy.json) + + +### Deploy using PowerShell + +Modify the snippet below to deploy Managed Application definition to a Resource Group in your Azure subscription + +````powershell +$rgname = "" +$location = "" +$authorization = ":" +$uri = "https://raw.githubusercontent.com/Azure/azure-managedapp-samples/master/Managed Application Sample Packages/101-managed-vm/managedvm.zip" + +New-AzureRmManagedApplicationDefinition -Name "ManagedVM" ` + -ResourceGroupName $rgname ` + -DisplayName "Managed VM" ` + -Description "Managed virtual machine" ` + -Location $location ` + -LockLevel ReadOnly ` + -PackageFileUri $uri ` + -Authorization $authorization ` + -Verbose +```` + +### Deploy using AzureCLI + +Modify the snippet below to deploy Managed Application definition to a Resource Group in your Azure subscription + +````azureCLI +az managedapp definition create \ + --name "ManagedVM" \ + --location \ + --resource-group \ + --lock-level ReadOnly \ + --display-name "Managed VM" \ + --description "Managed virtual machine" \ + --authorizations ":" \ + --package-file-uri "https://raw.githubusercontent.com/Azure/azure-managedapp-samples/master/Managed Application Sample Packages/101-managed-vm/managedvm.zip" +```` \ No newline at end of file diff --git a/Managed Application Sample Packages/101-managed-vm/azuredeploy.json b/Managed Application Sample Packages/101-managed-vm/azuredeploy.json new file mode 100644 index 0000000..1b79472 --- /dev/null +++ b/Managed Application Sample Packages/101-managed-vm/azuredeploy.json @@ -0,0 +1,82 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "type": "string", + "defaultValue": "ManagedWebApp", + "metadata": { + "description": "Provide a name for the managed application" + } + }, + "location": { + "type": "string", + "metadata": { + "description": "Specify the Azure region to place the application definition" + } + }, + "lockLevel": { + "type": "string", + "allowedValues": [ + "ReadOnly", + "CanNotDelete" + ], + "defaultValue": "ReadOnly", + "metadata": { + "description": "Specify the resource lock being used for the managed application" + } + }, + "authorizations": { + "type": "array", + "metadata": { + "description": "Provide the authorization mapping for the managed application." + } + }, + "description": { + "type": "string", + "defaultValue": "Managed Azure IaaS Web Application", + "metadata": { + "description": "Provide a brief description of the managed application" + } + }, + "displayName": { + "type": "string", + "defaultValue": "Managed Azure Web Application", + "metadata": { + "description": "Display name for the managed application" + } + } + }, + "variables": { + "packageFileUri": "[uri(deployment().properties.templateLink.uri, 'managedvm.zip')]" + }, + "resources": [ + { + "apiVersion": "2019-07-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "type": "Microsoft.Solutions/applicationDefinitions", + "properties": { + "lockLevel": "[parameters('lockLevel')]", + "authorizations": "[array(parameters('authorizations'))]", + "description": "[parameters('description')]", + "displayName": "[parameters('displayName')]", + "packageFileUri": "[variables('packageFileUri')]" + } + } + ], + "outputs": { + "managedApplicationName": { + "type": "string", + "value": "[parameters('name')]" + }, + "lockLevel": { + "type": "string", + "value": "[parameters('locklevel')]" + }, + "packageFileUri": { + "type": "string", + "value": "[variables('packageFileUri')]" + } + } +} \ No newline at end of file diff --git a/Managed Application Sample Packages/101-managed-vm/createUiDefinition.json b/Managed Application Sample Packages/101-managed-vm/createUiDefinition.json new file mode 100644 index 0000000..8e28175 --- /dev/null +++ b/Managed Application Sample Packages/101-managed-vm/createUiDefinition.json @@ -0,0 +1,131 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/0.1.2-preview/CreateUIDefinition.MultiVm.json#", + "handler": "Microsoft.Azure.CreateUIDef", + "version": "0.1.2-preview", + "parameters": { + "basics": [ + {} + ], + "steps": [ + { + "name": "credentialsConfig", + "label": "VM Credential", + "subLabel": { + "preValidation": "Configure the VM credentials", + "postValidation": "Done" + }, + "bladeTitle": "Credential", + "elements": [ + { + "name": "adminUsername", + "type": "Microsoft.Compute.UserNameTextBox", + "label": "User name", + "toolTip": "Admin username for the virtual machine", + "osPlatform": "Windows", + "constraints": { + "required": true + } + }, + { + "name": "adminPassword", + "type": "Microsoft.Compute.CredentialsCombo", + "label": { + "password": "Password", + "confirmPassword": "Confirm password" + }, + "toolTip": { + "password": "Admin password for the virtual machine" + }, + "osPlatform": "Windows", + "constraints": { + "required": true + } + } + ] + }, + { + "name": "vmConfig", + "label": "Virtual Machine settings", + "subLabel": { + "preValidation": "Configure the virtual machine settings", + "postValidation": "Done" + }, + "bladeTitle": "VM Settings", + "elements": [ + { + "name": "vmNamePrefix", + "type": "Microsoft.Common.TextBox", + "label": "Virtual Machine Name prefix", + "toolTip": "Prefix for the virtual machine", + "defaultValue": "", + "constraints": { + "required": true, + "regex": "[a-z][a-z0-9-]{2,5}[a-z0-9]$", + "validationMessage": "Must be 3-5 characters." + } + }, + { + "name": "vmSize", + "type": "Microsoft.Compute.SizeSelector", + "label": "Virtual machine size", + "toolTip": "The size of the virtual machine", + "recommendedSizes": [ + "Standard_D1_v2" + ], + "constraints": { + "allowedSizes": [ + "Standard_D1_v2" + ] + }, + "osPlatform": "Windows", + "count": 1 + } + ] + }, + { + "name": "webConfig", + "label": "Endpoint settings", + "subLabel": { + "preValidation": "Configure the VM endpoint", + "postValidation": "Done" + }, + "bladeTitle": "VM Endpoint settings", + "elements": [ + { + "name": "dnsAndPublicIP", + "type": "Microsoft.Network.PublicIpAddressCombo", + "label": { + "publicIpAddress": "Public IP address", + "domainNameLabel": "DNS label" + }, + "toolTip": { + "domainNameLabel": "DNS endpoint for the Managed VM IP address." + }, + "defaultValue": { + "publicIpAddressName": "ip01" + }, + "options": { + "hideNone": true, + "hideDomainNameLabel": false + }, + "constraints": { + "required": { + "domainNameLabel": true + } + } + } + ] + } + ], + "outputs": { + "location": "[location()]", + "vmSize": "[steps('vmConfig').vmSize]", + "vmNamePrefix": "[steps('vmConfig').vmNamePrefix]", + "applicationResourceName": "[steps('vmConfig').vmNamePrefix]", + "userName": "[steps('credentialsConfig').adminUsername]", + "pwd": "[steps('credentialsConfig').adminPassword.password]", + "dnsName": "[steps('webConfig').dnsAndPublicIP.domainNameLabel]", + "publicIPAddressName": "[steps('webConfig').dnsAndPublicIP.name]" + } + } +} \ No newline at end of file diff --git a/Managed Application Sample Packages/101-managed-vm/mainTemplate.json b/Managed Application Sample Packages/101-managed-vm/mainTemplate.json new file mode 100644 index 0000000..9d3581c --- /dev/null +++ b/Managed Application Sample Packages/101-managed-vm/mainTemplate.json @@ -0,0 +1,212 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "vmNamePrefix": { + "type": "string", + "metadata": { + "description": "Assign a prefix for the VM name" + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Select the Azure region for the resources" + } + }, + "vmSize": { + "type": "string", + "metadata": { + "description": "Selec the vm size" + } + }, + "userName": { + "type": "string", + "defaultValue": "azureadmin", + "metadata": { + "description": "Specify the OS username" + } + }, + "pwd": { + "type": "securestring", + "metadata": { + "description": "If Windows, specify the password for the OS username" + } + }, + "dnsName": { + "type": "string", + "metadata": { + "description": "Specify the DNS name for the managed web app" + } + }, + "publicIPAddressName": { + "type": "string", + "metadata": { + "description": "Assign a name for the public IP address" + } + } + }, + "variables": { + "vnetID": "[resourceId('Microsoft.Network/virtualnetworks', 'vmVnet')]", + "subnetRef": "[concat(variables('vnetID'),'/subnets/', 'subnet1')]", + "osTypeWindows": { + "imageOffer": "WindowsServer", + "imageSku": "2016-Datacenter", + "imagePublisher": "MicrosoftWindowsServer" + } + }, + "resources": [ + { + "type": "Microsoft.Network/virtualNetworks", + "apiVersion": "2019-11-01", + "name": "vmVnet", + "location": "[parameters('location')]", + "dependsOn": [ + "[resourceId('Microsoft.Network/networkSecurityGroups/', 'NSG')]" + ], + "properties": { + "addressSpace": { + "addressPrefixes": [ + "10.0.0.0/16" + ] + }, + "subnets": [ + { + "name": "subnet1", + "properties": { + "addressPrefix": "10.0.0.0/24", + "networkSecurityGroup": { + "id": "[resourceId('Microsoft.Network/networkSecurityGroups/', 'NSG')]" + } + } + } + ] + } + }, + { + "type": "Microsoft.Network/networkSecurityGroups", + "apiVersion": "2019-11-01", + "name": "NSG", + "location": "[parameters('location')]", + "properties": { + "securityRules": [ + { + "name": "RDP", + "properties": { + "access": "Allow", + "description": "Inbound RDP rule", + "direction": "Inbound", + "destinationAddressPrefix": "*", + "protocol": "Tcp", + "destinationPortRange": 3389, + "sourcePortRange": "*", + "priority": 500, + "sourceAddressPrefix": "*" + } + }, + { + "name": "HTTP", + "properties": { + "access": "Allow", + "description": "Inbound HTTP rule", + "direction": "Inbound", + "destinationAddressPrefix": "*", + "protocol": "Tcp", + "destinationPortRange": 80, + "sourcePortRange": "*", + "priority": 550, + "sourceAddressPrefix": "*" + } + } + ] + } + }, + { + "type": "Microsoft.Network/publicIPAddresses", + "apiVersion": "2019-11-01", + "name": "[concat(parameters('publicIPAddressName'), 'IP')]", + "location": "[parameters('location')]", + "properties": { + "publicIPallocationmethod": "Dynamic", + "dnsSettings": { + "domainNameLabel": "[toLower(parameters('dnsName'))]" + } + } + }, + { + "type": "Microsoft.Network/networkInterfaces", + "apiVersion": "2019-11-01", + "name": "[concat(parameters('vmNamePrefix'), 'nic')]", + "location": "[parameters('location')]", + "dependsOn": [ + "[concat('Microsoft.Network/publicIPAddresses/', parameters('publicIPAddressName'), 'IP')]", + "[resourceId('Microsoft.Network/virtualNetworks/', 'vmVnet')]" + ], + "properties": { + "ipConfigurations": [ + { + "name": "ipconfig1", + "properties": { + "privateIPAllocationMethod": "Dynamic", + "publicIPAddress": { + "id": "[resourceId('Microsoft.Network/publicIPAddresses', concat(parameters('publicIPAddressName'), 'IP'))]" + }, + "subnet": { + "id": "[variables('subnetRef')]" + } + } + } + ] + } + }, + { + "type": "Microsoft.Compute/virtualMachines", + "apiVersion": "2019-12-01", + "name": "[concat(parameters('vmNamePrefix'), '-app')]", + "location": "[parameters('location')]", + "dependsOn": [ + "[concat('Microsoft.Network/networkinterfaces/', parameters('vmNamePrefix'), 'nic')]" + ], + "properties": { + "hardwareProfile": { + "vmsize": "[parameters('vmSize')]" + }, + "osProfile": { + "computername": "[concat(parameters('vmNamePrefix'), '-app')]", + "adminusername": "[parameters('username')]", + "adminpassword": "[parameters('pwd')]" + }, + "storageProfile": { + "imageReference": { + "publisher": "[variables('osTypeWindows').imagePublisher]", + "offer": "[variables('osTypeWindows').imageOffer]", + "version": "latest", + "sku": "[variables('osTypeWindows').imageSku]" + }, + "osdisk": { + "name": "[concat(parameters('vmNamePrefix'), '-osDisk')]", + "createOption": "FromImage", + "managedDisk": { + "storageAccountType": "Standard_LRS" + }, + "caching": "ReadWrite" + } + }, + "networkprofile": { + "networkinterfaces": [ + { + "id": "[resourceId('Microsoft.Network/networkinterfaces', concat(parameters('vmNamePrefix'),'nic'))]" + } + ] + } + } + } + ], + "outputs": { + "vmEndpoint": { + "type": "string", + "value": "[reference(concat(parameters('publicIPAddressName'), 'IP')).dnsSettings.fqdn]" + } + } +} \ No newline at end of file diff --git a/Managed Application Sample Packages/101-managed-vm/managedvm.zip b/Managed Application Sample Packages/101-managed-vm/managedvm.zip new file mode 100644 index 0000000..9996257 Binary files /dev/null and b/Managed Application Sample Packages/101-managed-vm/managedvm.zip differ diff --git a/Managed Application Sample Packages/101-minimal-template/README.md b/Managed Application Sample Packages/101-minimal-template/README.md new file mode 100644 index 0000000..4092eb1 --- /dev/null +++ b/Managed Application Sample Packages/101-minimal-template/README.md @@ -0,0 +1,62 @@ +# Minimal Managed Application + +## Use case +You may have an application which you would like to deploy using the benefits of managed applications +via the Service Catalog or Azure Marketplace. Your existing deployment consists of something like the +following: +* [az CLI](https://docs.microsoft.com/cli/azure/install-azure-cli) +* [Azure PowerShell](https://docs.microsoft.com/powershell/azure/) +* [Terraform](https://www.terraform.io/docs/providers/azurerm/index.html) +* Or something else... + +Instead of writing an ARM template, you want to get the simplest thing working which let's you create a resource group and nothing more. The UI template collects one parameter (though you can collect more) and feeds that to the ARM template. These parameters can be inspected via the Azure Portal, az cli, or PowerShell. You can then use this to see which features of your chosen feature set do and do not work post install. For example, one can deploy Azure Kubernetes Service via the ARM template, but this same action will fail when executed as a contributor or owner to the managed resource group. This testing will inform you which items to move to the ARM template for your managed application. + +An example including a Terraform template is in [201-deploy-with-terraform](../201-deploy-with-terraform). + +>Note: This sample is for a minimal Managed Application in the Service Catalog. For Marketplace, please see these instructions: +[**Marketplace Managed Application**](https://docs.microsoft.com/azure/managed-applications/publish-marketplace-app) + +## Deploy this sample to your Service Catalog + +### Deploy using Azure Portal + +Clicking on the button below, will create the Managed Application definition to a Resource Group in your Azure subscription. + +[![Deploy to Azure](http://azuredeploy.net/deploybutton.png)](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2Fazure%2Fazure-managedapp-samples%2Fmaster%2FManaged%2520Application%2520Sample%2520Packages%2F101-minimal-template%2Fazuredeploy.json) + +### Deploy using PowerShell + +Modify the snippet below to deploy Managed Application definition to a Resource Group in your Azure subscription + +````powershell +$rgname = "" +$location = "" +$authorization = ":" +$uri = "https://raw.githubusercontent.com/Azure/azure-managedapp-samples/master/Managed Application Sample Packages/101-minimal-template/minimal-template.zip" + +New-AzureRmManagedApplicationDefinition -Name "MinimalTemplate" ` + -ResourceGroupName $rgname ` + -DisplayName "Minimal Template" ` + -Description "A minimal template" ` + -Location $location ` + -LockLevel ReadOnly ` + -PackageFileUri $uri ` + -Authorization $authorization ` + -Verbose +```` + +### Deploy using AzureCLI + +Modify the snippet below to deploy the Managed Application definition to a Resource Group in your Azure subscription. + +````azureCLI +az managedapp definition create \ + --name "MinimalTemplate" \ + --location \ + --resource-group \ + --lock-level ReadOnly \ + --display-name "Minimal Template" \ + --description "A minimal template" \ + --authorizations ":" \ + --package-file-uri "https://raw.githubusercontent.com/Azure/azure-managedapp-samples/master/Managed Application Sample Packages/101-minimal-template/minimal-template.zip" +```` \ No newline at end of file diff --git a/Managed Application Sample Packages/101-minimal-template/azuredeploy.json b/Managed Application Sample Packages/101-minimal-template/azuredeploy.json new file mode 100644 index 0000000..40e6ecb --- /dev/null +++ b/Managed Application Sample Packages/101-minimal-template/azuredeploy.json @@ -0,0 +1,82 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "type": "string", + "defaultValue": "ManagedWebApp", + "metadata": { + "description": "Provide a name for the managed application" + } + }, + "location": { + "type": "string", + "metadata": { + "description": "Specify the Azure region to place the application definition" + } + }, + "lockLevel": { + "type": "string", + "allowedValues": [ + "ReadOnly", + "CanNotDelete" + ], + "defaultValue": "ReadOnly", + "metadata": { + "description": "Specify the resource lock being used for the managed application" + } + }, + "authorizations": { + "type": "array", + "metadata": { + "description": "Provide the authorization mapping for the managed application." + } + }, + "description": { + "type": "string", + "defaultValue": "Managed Azure IaaS Web Application", + "metadata": { + "description": "Provide a brief description of the managed application" + } + }, + "displayName": { + "type": "string", + "defaultValue": "Managed Azure Web Application", + "metadata": { + "description": "Display name for the managed application" + } + } + }, + "variables": { + "packageFileUri": "[uri(deployment().properties.templateLink.uri, 'minimal-template.zip')]" + }, + "resources": [ + { + "apiVersion": "2019-07-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "type": "Microsoft.Solutions/applicationDefinitions", + "properties": { + "lockLevel": "[parameters('lockLevel')]", + "authorizations": "[array(parameters('authorizations'))]", + "description": "[parameters('description')]", + "displayName": "[parameters('displayName')]", + "packageFileUri": "[variables('packageFileUri')]" + } + } + ], + "outputs": { + "managedApplicationName": { + "type": "string", + "value": "[parameters('name')]" + }, + "lockLevel": { + "type": "string", + "value": "[parameters('locklevel')]" + }, + "packageFileUri": { + "type": "string", + "value": "[variables('packageFileUri')]" + } + } +} \ No newline at end of file diff --git a/Managed Application Sample Packages/101-minimal-template/createUiDefinition.json b/Managed Application Sample Packages/101-minimal-template/createUiDefinition.json new file mode 100644 index 0000000..99cfaf9 --- /dev/null +++ b/Managed Application Sample Packages/101-minimal-template/createUiDefinition.json @@ -0,0 +1,27 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/0.1.2-preview/CreateUIDefinition.MultiVm.json#", + "handler": "Microsoft.Azure.CreateUIDef", + "version": "0.1.2-preview", + "parameters": { + "basics": [ + { + "name": "username", + "type": "Microsoft.Common.TextBox", + "label": "User name", + "defaultValue": "", + "toolTip": "Use only allowed characters", + "constraints": { + "required": true, + "regex": "^[a-z0-9A-Z]{6,30}$", + "validationMessage": "Only alphanumeric characters are allowed, and the value must be 6-30 characters long." + }, + "visible": true + } + ], + "steps": [ + ], + "outputs": { + "username" : "[basics('username')]" + } + } + } \ No newline at end of file diff --git a/Managed Application Sample Packages/101-minimal-template/mainTemplate.json b/Managed Application Sample Packages/101-minimal-template/mainTemplate.json new file mode 100644 index 0000000..8e520b0 --- /dev/null +++ b/Managed Application Sample Packages/101-minimal-template/mainTemplate.json @@ -0,0 +1,27 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "username": { + "type": "string" + } + }, + "resources": [ + { + "apiVersion": "2019-10-01", + "name": "pid-", + "type": "Microsoft.Resources/deployments", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [] + } + } + } + ], + "outputs": { + + } +} \ No newline at end of file diff --git a/Managed Application Sample Packages/101-minimal-template/minimal-template.zip b/Managed Application Sample Packages/101-minimal-template/minimal-template.zip new file mode 100644 index 0000000..5267281 Binary files /dev/null and b/Managed Application Sample Packages/101-minimal-template/minimal-template.zip differ diff --git a/Managed Application Sample Packages/201-deploy-with-terraform/README.md b/Managed Application Sample Packages/201-deploy-with-terraform/README.md new file mode 100644 index 0000000..b954d26 --- /dev/null +++ b/Managed Application Sample Packages/201-deploy-with-terraform/README.md @@ -0,0 +1,55 @@ +# Deploy with Terraform + +## Use case +You have a Terraform template which you would like to use to deploy your managed application. You can use this project to understand which pieces of the deployment must move to the ARM template and which pieces may stay in Terraform. The example here deploys an empty resource group. The template is optimized for a demonstration and deploys everything for a basic virtual network except for the virtual machine(s). This item builds off of [101-minimal-template](../101-minimal-template) + +## Deploy this sample to your Service Catalog + +### Deploy using Azure Portal + +Clicking on the button below, will create the Managed Application definition to a Resource Group in your Azure subscription. + +[![Deploy to Azure](http://azuredeploy.net/deploybutton.png)](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2Fazure%2Fazure-managedapp-samples%2Fmaster%2FManaged%2520Application%2520Sample%2520Packages%2F201-deploy-with-terraform%2Fazuredeploy.json) + +### Deploy using PowerShell + +Modify the snippet below to deploy Managed Application definition to a Resource Group in your Azure subscription + +````powershell +$rgname = "" +$location = "" +$authorization = ":" +$uri = "https://raw.githubusercontent.com/Azure/azure-managedapp-samples/master/Managed Application Sample Packages/201-deploy-with-terraform/deploy-with-terraform.zip" + +New-AzureRmManagedApplicationDefinition -Name "MinimalTemplate" ` + -ResourceGroupName $rgname ` + -DisplayName "Minimal Template" ` + -Description "A minimal template" ` + -Location $location ` + -LockLevel ReadOnly ` + -PackageFileUri $uri ` + -Authorization $authorization ` + -Verbose +```` + +### Deploy using AzureCLI + +Modify the snippet below to deploy the Managed Application definition to a Resource Group in your Azure subscription. + +````azureCLI +az managedapp definition create \ + --name "MinimalTemplate" \ + --location \ + --resource-group \ + --lock-level ReadOnly \ + --display-name "Minimal Template" \ + --description "A minimal template" \ + --authorizations ":" \ + --package-file-uri "https://raw.githubusercontent.com/Azure/azure-managedapp-samples/master/Managed Application Sample Packages/201-deploy-with-terraform/deploy-with-terraform.zip" +```` + +### Run the Terraform template + +1. Update [variables.conf](./variables.conf) for your environment. The script does assume that you have created a Service Principal for managing your Managed Application or Service Catalog installations. Use the same identity as you used for the Authorization parameter for the Powershell call or teh authorizations parameter in the AzureCLI. + +1. Run [deploy.sh](./deploy.sh) from a bash shell. This will also work under [WSL](https://docs.microsoft.com/en-us/windows/wsl/wsl2-install). \ No newline at end of file diff --git a/Managed Application Sample Packages/201-deploy-with-terraform/azuredeploy.json b/Managed Application Sample Packages/201-deploy-with-terraform/azuredeploy.json new file mode 100644 index 0000000..d417a64 --- /dev/null +++ b/Managed Application Sample Packages/201-deploy-with-terraform/azuredeploy.json @@ -0,0 +1,82 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "type": "string", + "defaultValue": "ManagedWebApp", + "metadata": { + "description": "Provide a name for the managed application" + } + }, + "location": { + "type": "string", + "metadata": { + "description": "Specify the Azure region to place the application definition" + } + }, + "lockLevel": { + "type": "string", + "allowedValues": [ + "ReadOnly", + "CanNotDelete" + ], + "defaultValue": "ReadOnly", + "metadata": { + "description": "Specify the resource lock being used for the managed application" + } + }, + "authorizations": { + "type": "array", + "metadata": { + "description": "Provide the authorization mapping for the managed application." + } + }, + "description": { + "type": "string", + "defaultValue": "Managed Azure IaaS Web Application", + "metadata": { + "description": "Provide a brief description of the managed application" + } + }, + "displayName": { + "type": "string", + "defaultValue": "Managed Azure Web Application", + "metadata": { + "description": "Display name for the managed application" + } + } + }, + "variables": { + "packageFileUri": "[uri(deployment().properties.templateLink.uri, 'deploy-with-terraform.zip')]" + }, + "resources": [ + { + "apiVersion": "2019-07-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "type": "Microsoft.Solutions/applicationDefinitions", + "properties": { + "lockLevel": "[parameters('lockLevel')]", + "authorizations": "[array(parameters('authorizations'))]", + "description": "[parameters('description')]", + "displayName": "[parameters('displayName')]", + "packageFileUri": "[variables('packageFileUri')]" + } + } + ], + "outputs": { + "managedApplicationName": { + "type": "string", + "value": "[parameters('name')]" + }, + "lockLevel": { + "type": "string", + "value": "[parameters('locklevel')]" + }, + "packageFileUri": { + "type": "string", + "value": "[variables('packageFileUri')]" + } + } +} \ No newline at end of file diff --git a/Managed Application Sample Packages/201-deploy-with-terraform/createUiDefinition.json b/Managed Application Sample Packages/201-deploy-with-terraform/createUiDefinition.json new file mode 100644 index 0000000..99cfaf9 --- /dev/null +++ b/Managed Application Sample Packages/201-deploy-with-terraform/createUiDefinition.json @@ -0,0 +1,27 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/0.1.2-preview/CreateUIDefinition.MultiVm.json#", + "handler": "Microsoft.Azure.CreateUIDef", + "version": "0.1.2-preview", + "parameters": { + "basics": [ + { + "name": "username", + "type": "Microsoft.Common.TextBox", + "label": "User name", + "defaultValue": "", + "toolTip": "Use only allowed characters", + "constraints": { + "required": true, + "regex": "^[a-z0-9A-Z]{6,30}$", + "validationMessage": "Only alphanumeric characters are allowed, and the value must be 6-30 characters long." + }, + "visible": true + } + ], + "steps": [ + ], + "outputs": { + "username" : "[basics('username')]" + } + } + } \ No newline at end of file diff --git a/Managed Application Sample Packages/201-deploy-with-terraform/deploy-with-terraform.zip b/Managed Application Sample Packages/201-deploy-with-terraform/deploy-with-terraform.zip new file mode 100644 index 0000000..5267281 Binary files /dev/null and b/Managed Application Sample Packages/201-deploy-with-terraform/deploy-with-terraform.zip differ diff --git a/Managed Application Sample Packages/201-deploy-with-terraform/deploy.sh b/Managed Application Sample Packages/201-deploy-with-terraform/deploy.sh new file mode 100755 index 0000000..3507d0f --- /dev/null +++ b/Managed Application Sample Packages/201-deploy-with-terraform/deploy.sh @@ -0,0 +1,75 @@ +#!/bin/bash +echo "Running deploy.sh" +# Setup error handling +tempfiles=( ) +cleanup() { + rm -f "${tempfiles[@]}" +} +trap cleanup 0 + +error() { + local parent_lineno="$1" + local message="$2" + local code="${3:-1}" + if [[ -n "$message" ]] ; then + echo "Error on or near line ${parent_lineno}: ${message}; exiting with status ${code}" + else + echo "Error on or near line ${parent_lineno}; exiting with status ${code}" + fi +# az logout + + exit "${code}" +} +trap 'error ${LINENO}' ERR + +# so repeat runs work, removing terraform state. +# in production, you'd want one terraform state location per deployment +if [[ -f "terraform.tfstate" ]]; then + rm terraform.tfstate +fi +if [[ -f "terraform.tfstate.backup" ]]; then + rm terraform.tfstate.backup +fi + +# Load the config +echo "Loading config" +. variables.conf +azure_subscription_id=$(IFS='/' read -r -a parts <<< \ + "$managed_app_id" \ + && echo "${parts[2]}") +az login --service-principal -u $service_principal_id -p $service_principal_secret --tenant $azure_ad_tenant_id +az account set -s $azure_subscription_id + +# Login as the service principal +resource_group_location=$(az managedapp show --ids $managed_app_id \ + --query location --output tsv) + +managed_resource_group_id=$(az managedapp show --ids $managed_app_id \ + --query managedResourceGroupId --output tsv) + +managed_resource_group_name=$(IFS='/' read -r -a parts <<< \ + "$managed_resource_group_id" \ + && echo "${parts[-1]}") +echo $managed_resource_group_name + +# Copy over the local azurerm provider +# cp $GOPATH/bin/terraform-provider-azurerm ./.terraform/plugins/linux_amd64/terraform-provider-azurerm_v2.0.0_x5 + +echo "Checking terraform" +if [[ ! -d ".terraform" ]]; then + terraform init +fi +random_end=$(head /dev/urandom | tr -dc a-z | head -c 5) +base_name=$(echo $base_name)$(echo $random_end) +stg_account_name=$(echo $base_name)stg +echo $base_name +terraform apply -auto-approve -var="azure_ad_tenant_id=$azure_ad_tenant_id" \ + -var="azure_subscription_id=$azure_subscription_id" \ + -var="service_principal_secret=$service_principal_secret" \ + -var="service_principal_id=$service_principal_client_id" \ + -var="resource_group_name=$managed_resource_group_name" \ + -var="base_name=$base_name" \ + -var="location=$resource_group_location" \ + -var="stg_account_name=$stg_account_name" + +#az logout \ No newline at end of file diff --git a/Managed Application Sample Packages/201-deploy-with-terraform/main.tf b/Managed Application Sample Packages/201-deploy-with-terraform/main.tf new file mode 100644 index 0000000..3b9742d --- /dev/null +++ b/Managed Application Sample Packages/201-deploy-with-terraform/main.tf @@ -0,0 +1,99 @@ +# Configure the Microsoft Azure Provider +provider "azurerm" { + version = "=2.0.0" + subscription_id = var.azure_subscription_id + #client_id = var.service_principal_id + #client_secret = var.service_principal_secret + #tenant_id = var.azure_ad_tenant_id + skip_provider_registration = true + features{} +} + +# Create virtual network +resource "azurerm_virtual_network" "network" { + name = "${var.base_name}Vnet" + address_space = ["10.0.0.0/16"] + location = var.location + resource_group_name = var.resource_group_name + + tags = { + environment = "${var.base_name} Managed App" + } +} + +# Create subnet +resource "azurerm_subnet" "subnet" { + name = "${var.base_name}Subnet" + resource_group_name = var.resource_group_name + virtual_network_name = azurerm_virtual_network.network.name + address_prefix = "10.0.1.0/24" +} + +# Create public IPs +resource "azurerm_public_ip" "publicip" { + name = "${var.base_name}IP" + location = var.location + resource_group_name = var.resource_group_name + allocation_method = "Dynamic" + + tags = { + environment = "${var.base_name} Managed App" + } +} + +# Create Network Security Group and rule +resource "azurerm_network_security_group" "nsg" { + name = "${var.base_name}Nsg" + location = var.location + resource_group_name = var.resource_group_name + + security_rule { + name = "SSH" + priority = 1001 + direction = "Inbound" + access = "Allow" + protocol = "Tcp" + source_port_range = "*" + destination_port_range = "22" + source_address_prefix = "*" + destination_address_prefix = "*" + } + + tags = { + environment = "${var.base_name} Managed App" + } +} + +# Create network interface +resource "azurerm_network_interface" "nic" { + name = "${var.base_name}Nic" + location = var.location + resource_group_name = var.resource_group_name + #network_security_group_id = azurerm_network_security_group.nsg.id + + ip_configuration { + name = "${var.base_name}NicConfiguration" + subnet_id = azurerm_subnet.subnet.id + private_ip_address_allocation = "Dynamic" + public_ip_address_id = azurerm_public_ip.publicip.id + } + + tags = { + environment = "${var.base_name} Managed App" + } +} + +# Create storage account for boot diagnostics +resource "azurerm_storage_account" "diag" { + name = var.stg_account_name + resource_group_name = var.resource_group_name + location = var.location + account_tier = "Standard" + account_replication_type = "LRS" + account_kind = "BlobStorage" + + tags = { + environment = "${var.base_name} Managed App" + } +} + diff --git a/Managed Application Sample Packages/201-deploy-with-terraform/mainTemplate.json b/Managed Application Sample Packages/201-deploy-with-terraform/mainTemplate.json new file mode 100644 index 0000000..8e520b0 --- /dev/null +++ b/Managed Application Sample Packages/201-deploy-with-terraform/mainTemplate.json @@ -0,0 +1,27 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "username": { + "type": "string" + } + }, + "resources": [ + { + "apiVersion": "2019-10-01", + "name": "pid-", + "type": "Microsoft.Resources/deployments", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [] + } + } + } + ], + "outputs": { + + } +} \ No newline at end of file diff --git a/Managed Application Sample Packages/201-deploy-with-terraform/variables.conf b/Managed Application Sample Packages/201-deploy-with-terraform/variables.conf new file mode 100644 index 0000000..cb4ed43 --- /dev/null +++ b/Managed Application Sample Packages/201-deploy-with-terraform/variables.conf @@ -0,0 +1,15 @@ +# Azure account info +managed_app_id=/subscriptions//resourceGroups//providers/Microsoft.Solutions/applications/ + +# Azure Service Principal, created with +# az ad sp create-for-rbac --name +# Manage in Azure Active Directory under 'App registrations' +azure_ad_tenant_id= +service_principal_secret= +service_principal_client_id= +service_principal_id=http:// +# Where do you want to deploy? +resource_group_location= + +# What should the base name be for all of this? +base_name=scsmatest diff --git a/Managed Application Sample Packages/201-deploy-with-terraform/variables.tf b/Managed Application Sample Packages/201-deploy-with-terraform/variables.tf new file mode 100644 index 0000000..9c1ab5e --- /dev/null +++ b/Managed Application Sample Packages/201-deploy-with-terraform/variables.tf @@ -0,0 +1,39 @@ +variable "azure_ad_tenant_id" { + type = string + default = "" +} + +variable "azure_subscription_id" { + type = string + default = "" +} + +variable "service_principal_secret" { + type = string + default = "" +} + +variable "service_principal_id" { + type = string + default = "" +} + +variable "location" { + type = string + default = "centralus" +} + +variable "base_name" { + type = string + default = "dummybase" +} + +variable "stg_account_name" { + type = string + default = "" +} + +variable "resource_group_name" { + type = string + default = "" +} \ No newline at end of file diff --git a/Managed Application Sample Packages/201-managed-app-using-existing-vnet/README.md b/Managed Application Sample Packages/201-managed-app-using-existing-vnet/README.md new file mode 100644 index 0000000..383f032 --- /dev/null +++ b/Managed Application Sample Packages/201-managed-app-using-existing-vnet/README.md @@ -0,0 +1,59 @@ +# Managed Application (Trial or Production) into a new or existing virtual network + +This Managed Application supports demonstrates how you can create flexible deployment options for customers, using native ARM template language expressions, together with UI elements. + +* New or Existing virtual network? + +This managed application can either be deployed to a new virtual network the customer specifices, or plug into an existing virtual network. + +* Trial or Production? + +Let your customer explore the managed application using trial, where they will run an implementation with minimal cost and footprint. If they opt-in for production, they will get the optimized experienc which can have additional costs (vm size, additional resources for HA etc.) + +>Note: This sample is for Managed Application in Service Catalog. For Marketplace, please see these instructions: +[**Marketplace Managed Application**](https://docs.microsoft.com/en-us/azure/managed-applications/publish-marketplace-app) + +## Deploy this sample to your Service Catalog + +### Deploy using Azure Portal + +Clicking on the button below, will create the Managed Application definition to a Resource Group in your Azure subscription. + +[![Deploy to Azure](http://azuredeploy.net/deploybutton.png)](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2Fazure%2Fazure-managedapp-samples%2Fmaster%2FManaged%2520Application%2520Sample%2520Packages%2F201-managed-app-using-existing-vnet%2Fazuredeploy.json) + +### Deploy using PowerShell + +Modify the snippet below to deploy Managed Application definition to a Resource Group in your Azure subscription + +````powershell +$rgname = "" +$location = "" +$authorization = ":" +$uri = "https://raw.githubusercontent.com/Azure/azure-managedapp-samples/master/Managed Application Sample Packages/201-managed-app-using-existing-vnet/managedAppVnet.zip" + +New-AzureRmManagedApplicationDefinition -Name "ManagedWebApp" ` + -ResourceGroupName $rgname ` + -DisplayName "Managed Web App" ` + -Description "Managed Web App with Azure mgmt" ` + -Location $location ` + -LockLevel ReadOnly ` + -PackageFileUri $uri ` + -Authorization $authorization ` + -Verbose +```` + +### Deploy using AzureCLI + +Modify the snippet below to deploy Managed Application definition to a Resource Group in your Azure subscription + +````azureCLI +az managedapp definition create \ + --name "ManagedWebApp" \ + --location \ + --resource-group \ + --lock-level ReadOnly \ + --display-name "Managed Web Application" \ + --description "Web App with Azure mgmt" \ + --authorizations ":" \ + --package-file-uri "https://raw.githubusercontent.com/Azure/azure-managedapp-samples/master/Managed Application Sample Packages/201-managed-app-using-existing-vnet/managedAppVnet.zip" +```` \ No newline at end of file diff --git a/Managed Application Sample Packages/201-managed-app-using-existing-vnet/azuredeploy.json b/Managed Application Sample Packages/201-managed-app-using-existing-vnet/azuredeploy.json new file mode 100644 index 0000000..5cf535e --- /dev/null +++ b/Managed Application Sample Packages/201-managed-app-using-existing-vnet/azuredeploy.json @@ -0,0 +1,82 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "type": "string", + "defaultValue": "ManagedServiceFabric", + "metadata": { + "description": "Provide a name for the managed application" + } + }, + "location": { + "type": "string", + "metadata": { + "description": "Specify the Azure region to place the application definition" + } + }, + "lockLevel": { + "type": "string", + "allowedValues": [ + "ReadOnly", + "CanNotDelete" + ], + "defaultValue": "ReadOnly", + "metadata": { + "description": "Specify the resource lock being used for the managed application" + } + }, + "authorizations": { + "type": "array", + "metadata": { + "description": "Provide the authorization mapping for the managed application." + } + }, + "description": { + "type": "string", + "defaultValue": "Managed Azure Service Fabric with Azure mgmt services", + "metadata": { + "description": "Provide a brief description of the managed application" + } + }, + "displayName": { + "type": "string", + "defaultValue": "Managed Azure Service Fabric", + "metadata": { + "description": "Display name for the managed application" + } + } + }, + "variables": { + "packageFileUri": "[uri(deployment().properties.templateLink.uri, 'managedservicefabric.zip')]" + }, + "resources": [ + { + "apiVersion": "2017-09-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "type": "Microsoft.Solutions/applicationDefinitions", + "properties": { + "lockLevel": "[parameters('lockLevel')]", + "authorizations": "[array(parameters('authorizations'))]", + "description": "[parameters('description')]", + "displayName": "[parameters('displayName')]", + "packageFileUri": "[variables('packageFileUri')]" + } + } + ], + "outputs": { + "managedApplicationName": { + "type": "string", + "value": "[parameters('name')]" + }, + "lockLevel": { + "type": "string", + "value": "[parameters('locklevel')]" + }, + "packageFileUri": { + "type": "string", + "value": "[variables('packageFileUri')]" + } + } +} \ No newline at end of file diff --git a/Managed Application Sample Packages/201-managed-app-using-existing-vnet/createUiDefinition.json b/Managed Application Sample Packages/201-managed-app-using-existing-vnet/createUiDefinition.json new file mode 100644 index 0000000..0f2fda6 --- /dev/null +++ b/Managed Application Sample Packages/201-managed-app-using-existing-vnet/createUiDefinition.json @@ -0,0 +1,140 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/0.1.2-preview/CreateUIDefinition.MultiVm.json#", + "handler": "Microsoft.Azure.CreateUIDef", + "version": "0.1.2-preview", + "parameters": { + "basics": [ + {} + ], + "steps": [ + { + "name": "vmCredentials", + "label": "Azure Managed Application", + "bladeTitle": "Application credentials", + "subLabel": { + "preValidation": "Provide VM credentials", + "postValidation": "Great - let's move on!" + }, + "elements": [ + { + "name": "adminUserName", + "type": "Microsoft.Compute.UserNameTextBox", + "label": "Admin username", + "osPlatform": "Windows", + "constraints": { + "required": true + }, + "toolTip": "Provide admin username for the virtual machine" + }, + { + "name": "vmPwd", + "type": "Microsoft.Compute.CredentialsCombo", + "label": { + "password": "Password", + "confirmPassword": "Confirm password" + }, + "osPlatform": "Windows", + "options": { + "hideConfirmation": false + }, + "constraints": { + "required": true + }, + "toolTip": { + "password": "Provide admin password for the virtual machine" + }, + "visible": true + } + ] + }, + { + "name": "appSettings", + "label": "Application settings", + "subLabel": { + "preValidation": "Configure the managed application", + "postValidation": "Done!" + }, + "bladeTitle": "Settings", + "elements": [ + { + "name": "vmName", + "type": "Microsoft.Common.TextBox", + "label": "Application name", + "toolTip": "Assign a name to your Azure application", + "defaultValue": "", + "constraints": { + "required": true, + "regex": "^[a-z][a-z0-9-]{1,3}[a-z0-9]$", + "validationMessage": "Must be 3-5 characters." + } + }, + { + "name": "trialOrProd", + "type": "Microsoft.Common.OptionsGroup", + "label": "Trial or Production?", + "defaultValue": "Trial", + "toolTip": "For trial, cost will be minimal. For prod, resources are optimized.", + "constraints": { + "allowedValues": [ + { + "label": "Trial", + "value": "Trial" + }, + { + "label": "Production", + "value": "Production" + } + ] + }, + "visible": true + }, + { + "name": "virtualNetwork", + "type": "Microsoft.Network.VirtualNetworkCombo", + "label": { + "virtualNetwork": "Virtual network", + "subnets": "Subnets" + }, + "toolTip": { + "virtualNetwork": "Virtual Network Name", + "subnets": "Subnet requried for Azure Application" + }, + "defaultValue": { + "name": "app-vnet", + "addressPrefixSize": "/22" + }, + "constraints": { + "minAddressPrefixSize": "/22" + }, + "subnets": { + "subnet1": { + "label": "Subnet name", + "defaultValue": { + "name": "app-subnet", + "addressPrefixSize": "/24" + }, + "constraints": { + "minAddressPrefixSize": "/24", + "minAddressCount": 12, + "requireContiguousAddresses": false + } + } + } + } + ] + } + ], + "outputs": { + "vmName": "[steps('appSettings').vmName]", + "trialOrProduction": "[steps('appSettings').trialOrProd]", + "userName": "[steps('vmCredentials').adminUsername]", + "pwd": "[steps('vmCredentials').vmPwd.password]", + "applicationResourceName": "[steps('appSettings').vmName]", + "subnetName": "[steps('appSettings').virtualNetwork.subnets.subnet1.name]", + "subnetAddressPrefix": "[steps('appSettings').virtualNetwork.subnets.subnet1.addressPrefix]", + "vNetAddressPrefix": "[steps('appSettings').virtualNetwork.addressPrefix]", + "virtualNetworkName": "[steps('appSettings').virtualNetwork.name]", + "vNetRgName": "[steps('appSettings').virtualNetwork.resourceGroup]" + } + } +} \ No newline at end of file diff --git a/Managed Application Sample Packages/201-managed-app-using-existing-vnet/mainTemplate.json b/Managed Application Sample Packages/201-managed-app-using-existing-vnet/mainTemplate.json new file mode 100644 index 0000000..27b8fab --- /dev/null +++ b/Managed Application Sample Packages/201-managed-app-using-existing-vnet/mainTemplate.json @@ -0,0 +1,249 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "vmName": { + "type": "string", + "metadata": { + "description": "Virtual machine name" + } + }, + "trialOrProduction": { + "type": "string", + "allowedValues": [ + "Trial", + "Production" + ], + "metadata": { + "description": "Select whether the VM should be in production or not." + } + }, + "userName": { + "type": "string", + "defaultValue": "azureadmin", + "metadata": { + "description": "Username for the guest OS" + } + }, + "pwd": { + "type": "securestring", + "metadata": { + "description": "Application password" + } + }, + "virtualNetworkName": { + "type": "string", + "defaultValue": "vnet1", + "metadata": { + "description": "Virtual network name" + } + }, + "subnetName": { + "type": "string", + "defaultValue": "subnet1", + "metadata": { + "description": "Subnet name" + } + }, + "vNetRgName": { + "type": "string", + "metadata": { + "description": "Virtual network resource group name" + } + }, + "vNetAddressPrefix": { + "type": "string", + "defaultValue": "192.168.0.0/16", + "metadata": { + "description": "Virtual network address prefix" + } + }, + "subnetAddressPrefix": { + "type": "string", + "metadata": { + "description": "Subnet address prefix" + } + } + }, + "variables": { + "diskSizes": [20, 30], + "productionvNetName": "[resourceId(parameters('vNetRgName'), 'Microsoft.Networks/virtualNetworks/', parameters('virtualNetworkName'))]", + "productionSubnetId": "[concat(variables('productionvNetName'), '/subnets/', parameters('subnetName'))]", + "vNetName": "trialvNet", + "sNetName": "trialSubnet", + "vnetID": "[resourceId('Microsoft.Network/virtualnetworks', variables('vNetName'))]", + "subnetRef": "[concat(variables('vNetId'),'/subnets/', variables('sNetName'))]", + "windowsOffer": "WindowsServer", + "windowsSku": "2016-Datacenter", + "windowsPublisher": "MicrosoftWindowsServer", + "availabilitySetName": "[concat(parameters('vmName'), '-', 'avset')]", + "availabilitySetId": { + "id": "[resourceId('Microsoft.Compute/availabilitySets', variables('availabilitySetName'))]" + }, + "vNicName": "[concat(parameters('vmName'), '-', 'nic')]", + "pNicName": "[concat(parameters('vmName'), '-', 'pip')]", + "copy": [ + { + "name": "managedDiskId", + "count": "[length(variables('diskSizes'))]", + "input": { + "lun": "[copyIndex('managedDiskId')]", + "createOption": "Empty", + "diskSizeGB": "[variables('diskSizes')[copyIndex('managedDiskId')]]" + } + } + ] + }, + "resources": [ + { + "condition": "[equals(parameters('trialOrProduction'), 'Trial')]", + "apiVersion": "2019-11-01", + "type": "Microsoft.Network/virtualNetworks", + "name": "[variables('vNetName')]", + "location": "[resourceGroup().location]", + "properties": { + "addressSpace": { + "addressPrefixes": [ + "[parameters('vNetAddressPrefix')]" + ] + }, + "subnets": [ + { + "name": "[variables('sNetName')]", + "properties": { + "addressPrefix": "[parameters('subnetAddressPrefix')]" + } + } + ] + } + }, + { + "type": "Microsoft.Network/publicIPAddresses", + "apiVersion": "2019-11-01", + "name": "[variables('pNicName')]", + "location": "[resourceGroup().location]", + "properties": { + "publicIPallocationmethod": "Dynamic", + "dnsSettings": { + "domainNameLabel": "[toLower(parameters('vmName'))]" + } + } + }, + { + "type": "Microsoft.Network/networkInterfaces", + "apiVersion": "2019-11-01", + "name": "[variables('vNicName')]", + "location": "[resourceGroup().location]", + "dependsOn": [ + "[resourceId('Microsoft.Network/publicIPAddresses/', variables('pNicName'))]", + "[resourceId('Microsoft.network/virtualNetworks/', variables('vNetName'))]" + ], + "properties": { + "ipConfigurations": [ + { + "name": "ipconfig1", + "properties": { + "privateIPAllocationMethod": "Dynamic", + "publicIPAddress": { + "id": "[resourceId('Microsoft.Network/publicIPAddresses', variables('pNicName'))]" + }, + "subnet": { + "id": "[if(equals(parameters('trialOrProduction'), 'Production'), variables('productionSubnetId'), variables('subnetRef'))]" + } + } + } + ] + } + }, + { + "condition": "[equals(parameters('trialOrProduction'), 'Production')]", + "type": "Microsoft.Compute/availabilitySets", + "apiVersion": "2019-12-01", + "name": "[variables('availabilitySetName')]", + "location": "[resourceGroup().location]", + "properties": { + "platformFaultDomainCount": 2, + "platformUpdateDomainCount": 3 + }, + "sku": { + "name": "Aligned" + } + }, + { + "condition": "[equals(parameters('trialOrProduction'), 'Production')]", + "type": "Microsoft.Compute/disks", + "apiVersion": "2019-12-01", + "name": "[concat(parameters('vmName'), '-mdisk')]", + "location": "[resourceGroup().location]", + "sku": { + "name": "Premium_LRS" + }, + "properties": { + "creationData": { + "createOption": "Empty" + }, + "diskSizeGB": 200 + } + }, + { + "type": "Microsoft.Compute/virtualMachines", + "apiVersion": "2019-12-01", + "name": "[parameters('vmName')]", + "location": "[resourceGroup().location]", + "dependsOn": [ + "[resourceId('Microsoft.Network/networkinterfaces/', variables('vNicName'))]", + "[resourceId('Microsoft.Compute/availabilitySets/', variables('availabilitySetName'))]", + "[resourceId('Microsoft.Compute/disks', concat(parameters('vmName'), '-mDisk'))]" + ], + "properties": { + "availabilitySet": "[if(equals(parameters('trialOrProduction'), 'Production'), variables('availabilitySetId'), json('null'))]", + "hardwareprofile": { + "vmsize": "[if(equals(parameters('trialOrProduction'), 'Production'), 'Standard_DS3_v2', 'Standard_DS1_v2')]" + }, + "osProfile": { + "computername": "[parameters('vmName')]", + "adminusername": "[parameters('username')]", + "adminpassword": "[parameters('pwd')]" + }, + "storageProfile": { + "imageReference": { + "publisher": "[variables('windowsPublisher')]", + "offer": "[variables('windowsOffer')]", + "version": "latest", + "sku": "[variables('windowsSku')]" + }, + "osdisk": { + "name": "[concat(parameters('vmName'), '-osDisk')]", + "createOption": "FromImage", + "managedDisk": { + "storageAccountType": "[if(equals(parameters('trialOrProduction'), 'Production'), 'Premium_LRS', 'Standard_LRS')]" + }, + "caching": "ReadWrite" + }, + "dataDisks": "[if(equals(parameters('trialOrProduction'), 'Production'), variables('managedDiskId'), json('null'))]" + }, + "networkprofile": { + "networkinterfaces": [ + { + "id": "[resourceId('Microsoft.Network/networkinterfaces', variables('vNicName'))]" + } + ] + } + } + } + ], + "outputs": { + "connectionInfo": { + "type": "string", + "value": "Use RDP to connect to the VM endpoint" + }, + "vmEndpoint": { + "type": "string", + "value": "[reference(concat(variables('pNicName'))).dnsSettings.fqdn]" + }, + "environment":{ + "type": "string", + "value": "[if(equals(parameters('trialOrProduction'), 'Trial'), 'This is a trial', 'Production')]" + } + } +} \ No newline at end of file diff --git a/Managed Application Sample Packages/201-managed-app-using-existing-vnet/managedAppVnet.zip b/Managed Application Sample Packages/201-managed-app-using-existing-vnet/managedAppVnet.zip new file mode 100644 index 0000000..7249f04 Binary files /dev/null and b/Managed Application Sample Packages/201-managed-app-using-existing-vnet/managedAppVnet.zip differ diff --git a/Managed Application Sample Packages/201-managed-service-fabric/azuredeploy.json b/Managed Application Sample Packages/201-managed-service-fabric/azuredeploy.json new file mode 100644 index 0000000..5cf535e --- /dev/null +++ b/Managed Application Sample Packages/201-managed-service-fabric/azuredeploy.json @@ -0,0 +1,82 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "type": "string", + "defaultValue": "ManagedServiceFabric", + "metadata": { + "description": "Provide a name for the managed application" + } + }, + "location": { + "type": "string", + "metadata": { + "description": "Specify the Azure region to place the application definition" + } + }, + "lockLevel": { + "type": "string", + "allowedValues": [ + "ReadOnly", + "CanNotDelete" + ], + "defaultValue": "ReadOnly", + "metadata": { + "description": "Specify the resource lock being used for the managed application" + } + }, + "authorizations": { + "type": "array", + "metadata": { + "description": "Provide the authorization mapping for the managed application." + } + }, + "description": { + "type": "string", + "defaultValue": "Managed Azure Service Fabric with Azure mgmt services", + "metadata": { + "description": "Provide a brief description of the managed application" + } + }, + "displayName": { + "type": "string", + "defaultValue": "Managed Azure Service Fabric", + "metadata": { + "description": "Display name for the managed application" + } + } + }, + "variables": { + "packageFileUri": "[uri(deployment().properties.templateLink.uri, 'managedservicefabric.zip')]" + }, + "resources": [ + { + "apiVersion": "2017-09-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "type": "Microsoft.Solutions/applicationDefinitions", + "properties": { + "lockLevel": "[parameters('lockLevel')]", + "authorizations": "[array(parameters('authorizations'))]", + "description": "[parameters('description')]", + "displayName": "[parameters('displayName')]", + "packageFileUri": "[variables('packageFileUri')]" + } + } + ], + "outputs": { + "managedApplicationName": { + "type": "string", + "value": "[parameters('name')]" + }, + "lockLevel": { + "type": "string", + "value": "[parameters('locklevel')]" + }, + "packageFileUri": { + "type": "string", + "value": "[variables('packageFileUri')]" + } + } +} \ No newline at end of file diff --git a/Managed Application Sample Packages/201-managed-service-fabric/createUiDefinition.json b/Managed Application Sample Packages/201-managed-service-fabric/createUiDefinition.json new file mode 100644 index 0000000..3231697 --- /dev/null +++ b/Managed Application Sample Packages/201-managed-service-fabric/createUiDefinition.json @@ -0,0 +1,120 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/0.1.2-preview/CreateUIDefinition.MultiVm.json#", + "handler": "Microsoft.Azure.CreateUIDef", + "version": "0.1.2-preview", + "parameters": { + "basics": [ + { + "name": "serviceFabricName", + "type": "Microsoft.Common.TextBox", + "label": "Service Fabric Cluster Name", + "defaultValue": "", + "constraints": { + "required": true, + "regex": "^[a-z0-9A-Z]{1,10}$", + "validationMessage": "Only letters and numbers are allowed, and the value must be 1-10 characters long." + } + } + ], + "steps": [ + { + "name": "infrastructureConfig", + "label": "Infrastructure settings", + "subLabel": { + "preValidation": "Configure the infrastructure settings", + "postValidation": "Done" + }, + "bladeTitle": "Infrastructure settings", + "elements": [ + { + "name": "adminUsername", + "type": "Microsoft.Compute.UserNameTextBox", + "label": "Username", + "toolTip": "Admin username for the virtual machines.", + "osPlatform": "Windows", + "constraints": { + "required": true + } + }, + { + "name": "adminPassword", + "type": "Microsoft.Compute.CredentialsCombo", + "label": { + "password": "Password", + "confirmPassword": "Confirm password" + }, + "toolTip": { + "password": "Admin password for the virtual machines and the Active Directory domain." + }, + "osPlatform": "Windows", + "constraints": { + "required": true + } + }, + { + "name": "premiumMgmt", + "type": "Microsoft.Common.OptionsGroup", + "label": "Enable premium management of your Service Fabric Cluster?", + "defaultValue": "Yes", + "toolTip": "Select Yes to set up premium Azure management for your Service Fabric cluster", + "constraints": { + "allowedValues": [ + { + "label": "Yes", + "value": "Yes" + }, + { + "label": "No", + "value": "No" + } + ] + }, + "visible": true + } + ] + }, + { + "name": "sfConfig", + "label": "Service Fabric Cluster settings", + "subLabel": { + "preValidation": "Configure the Service Fabric Cluster", + "postValidation": "Done" + }, + "bladeTitle": "Service Fabric Cluster settings", + "elements": [ + { + "name": "sfDnsAndPublicIP", + "type": "Microsoft.Network.PublicIpAddressCombo", + "label": { + "publicIpAddress": "Public IP address", + "domainNameLabel": "DNS label" + }, + "toolTip": { + "domainNameLabel": "DNS endpoint for the Service Fabric public IP address, which hosts your microservices." + }, + "defaultValue": { + "publicIpAddressName": "ip01" + }, + "options": { + "hideNone": true, + "hideDomainNameLabel": false + }, + "constraints": { + "required": { + "domainNameLabel": true + } + } + } + ] + } + ], + "outputs": { + "computeLocation": "[location()]", + "clusterName": "[basics('serviceFabricName')]", + "adminUsername": "[steps('infrastructureConfig').adminUsername]", + "adminPassword": "[steps('infrastructureConfig').adminPassword.password]", + "dnsName": "[steps('sfConfig').sfDnsAndPublicIP.domainNameLabel]", + "enablePremiumManagement":"[steps('infrastructureConfig').premiumMgmt]" + } + } +} diff --git a/Managed Application Sample Packages/201-managed-service-fabric/images/appliance.png b/Managed Application Sample Packages/201-managed-service-fabric/images/appliance.png new file mode 100644 index 0000000..574febc Binary files /dev/null and b/Managed Application Sample Packages/201-managed-service-fabric/images/appliance.png differ diff --git a/Managed Application Sample Packages/201-managed-service-fabric/mainTemplate.json b/Managed Application Sample Packages/201-managed-service-fabric/mainTemplate.json new file mode 100644 index 0000000..1f5d153 --- /dev/null +++ b/Managed Application Sample Packages/201-managed-service-fabric/mainTemplate.json @@ -0,0 +1,911 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "clusterName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Name of your cluster - Between 3 and 23 characters. Letters and numbers only" + } + }, + "computeLocation": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Select the location for your SF resources" + } + }, + "adminUserName": { + "type": "string", + "defaultValue": "azureadmin", + "metadata": { + "description": "Remote desktop user Id" + } + }, + "adminPassword": { + "type": "securestring", + "metadata": { + "description": "Remote desktop user password. Must be a strong password" + } + }, + "dnsName": { + "type": "string", + "defaultvalue": "", + "metadata": { + "description": "DNS name for your Service Fabric Cluster endpoint" + } + }, + "overProvision": { + "type": "string", + "defaultValue": "false", + "metadata": { + "description": "true or false" + } + }, + "vmNodeType0Name": { + "type": "string", + "defaultValue": "sfvmss", + "maxLength": 9, + "metadata": { + "description": "Specify type name" + } + }, + "enablePremiumManagement": { + "type": "string", + "allowedValues": [ + "Yes", + "No" + ], + "metadata": { + "description": "Enable premium management of your Service Fabric Cluster" + } + } + }, + "variables": { + "addressPrefix": "10.0.0.0/16", + "omsWorkspaceName": "[concat(uniqueString(resourceGroup().Id), '-', 'oms')]", + "subnet0Prefix": "10.0.0.0/24", + "nicName": "[concat('nic', uniqueString(resourceGroup().Id))]", + "lbIPName": "[concat('lb', uniqueString(resourceGroup().Id))]", + "storageAccountType": "Standard_LRS", + "vmImageVersion": "latest", + "vmImageSku": "2016-Datacenter", + "vmImageOffer": "WindowsServer", + "vmNodeType0Size": "Standard_D1_v2", + "vmImagePublisher": "MicrosoftWindowsServer", + "nt0applicationStartPort": 20000, + "nt0applicationEndPort": 30000, + "nt0ephemeralStartPort": 49152, + "nt0ephemeralEndPort": 65534, + "nt0fabricTcpGatewayPort": 19000, + "nt0fabricHttpGatewayPort": 19080, + "subnet0Name": "Subnet-0", + "virtualNetworkName": "Vnet1", + "supportLogStorageAccountName": "[toLower(concat('sf', uniqueString(resourceGroup().id),'2'))]", + "applicationDiagnosticsStorageAccountType": "Standard_LRS", + "applicationDiagnosticsStorageAccountName": "[toLower(concat('oms', uniqueString(resourceGroup().id), '3' ))]", + "omsSolution": { + "batch": [ + { + "solutionName": "[concat('Containers', '(', variables('omsWorkspacename'), ')')]", + "solution": "Containers" + }, + { + "solutionName": "[concat('ServiceFabric', '(', variables('omsWorkspacename'), ')')]", + "solution": "ServiceFabric" + } + ] + }, + "omsLocation": { + "eastasia": "southeastasia", + "southeastasia": "southeastasia", + "centralus": "westcentralus", + "eastus": "eastus", + "eastus2": "eastus", + "westus": "westcentralus", + "northcentralus": "westcentralus", + "southcentralus": "westcentralus", + "northeurope": "westeurope", + "westeurope": "westeurope", + "japanwest": "southeastasia", + "japaneast": "southeastasia", + "brazilsouth": "eastus", + "australiaeast": "australiasoutheast", + "australiasoutheast": "australiasoutheast", + "southindia": "southeastasia", + "centralindia": "southeastasia", + "westindia": "southeastasia", + "canadacentral": "eastus", + "canadaeast": "eastus", + "uksouth": "westeurope", + "ukwest": "westeurope", + "westcentralus": "westcentralus", + "westus2": "westcentralus", + "koreacentral": "southeastasia", + "koreasouth": "southeastasia", + "eastus2euap": "eastus" + }, + "omsWorkspaceLocation": "[variables('omsLocation')[parameters('computeLocation')]]", + "vnetID": "[resourceId('Microsoft.Network/virtualNetworks',variables('virtualNetworkName'))]", + "subnet0Ref": "[concat(variables('vnetID'),'/subnets/',variables('subnet0Name'))]", + "lbID0": "[resourceId('Microsoft.Network/loadBalancers', concat('LB','-', parameters('clusterName'),'-',parameters('vmNodeType0Name')))]", + "lbIPConfig0": "[concat(variables('lbID0'),'/frontendIPConfigurations/LoadBalancerIPConfig')]", + "lbPoolID0": "[concat(variables('lbID0'),'/backendAddressPools/LoadBalancerBEAddressPool')]", + "lbProbeID0": "[concat(variables('lbID0'),'/probes/FabricGatewayProbe')]", + "lbHttpProbeID0": "[concat(variables('lbID0'),'/probes/FabricHttpGatewayProbe')]", + "lbNatPoolID0": "[concat(variables('lbID0'),'/inboundNatPools/LoadBalancerBEAddressNatPool')]", + "vmStorageAccountName0": "[toLower(concat(uniqueString(resourceGroup().id), '1', '0' ))]", + "uniqueStringArray0": [ + "[concat(variables('vmStorageAccountName0'), '0')]", + "[concat(variables('vmStorageAccountName0'), '1')]", + "[concat(variables('vmStorageAccountName0'), '2')]", + "[concat(variables('vmStorageAccountName0'), '3')]", + "[concat(variables('vmStorageAccountName0'), '4')]" + ] + }, + "resources": [ + { + "apiVersion": "2019-06-01", + "type": "Microsoft.Storage/storageAccounts", + "name": "[variables('supportLogStorageAccountName')]", + "location": "[parameters('computeLocation')]", + "kind": "Storage", + "sku": { + "name": "[variables('storageAccountType')]" + } + }, + { + "apiVersion": "2019-06-01", + "type": "Microsoft.Storage/storageAccounts", + "name": "[variables('applicationDiagnosticsStorageAccountName')]", + "location": "[parameters('computeLocation')]", + "kind": "Storage", + "sku": { + "name": "[variables('applicationDiagnosticsStorageAccountType')]" + } + }, + { + "apiVersion": "2019-11-01", + "type": "Microsoft.Network/virtualNetworks", + "name": "[variables('virtualNetworkName')]", + "location": "[parameters('computeLocation')]", + "properties": { + "addressSpace": { + "addressPrefixes": [ + "[variables('addressPrefix')]" + ] + }, + "subnets": [ + { + "name": "[variables('subnet0Name')]", + "properties": { + "addressPrefix": "[variables('subnet0Prefix')]" + } + } + ] + }, + "tags": { + "resourceType": "Service Fabric", + "clusterName": "[parameters('clusterName')]" + } + }, + { + "apiVersion": "2019-11-01", + "type": "Microsoft.Network/publicIPAddresses", + "name": "[concat(variables('lbIPName'),'-','0')]", + "location": "[parameters('computeLocation')]", + "properties": { + "dnsSettings": { + "domainNameLabel": "[parameters('dnsName')]" + }, + "publicIPAllocationMethod": "Dynamic" + }, + "tags": { + "resourceType": "Service Fabric", + "clusterName": "[parameters('clusterName')]" + } + }, + { + "apiVersion": "2019-11-01", + "type": "Microsoft.Network/loadBalancers", + "name": "[concat('LB','-', parameters('clusterName'),'-',parameters('vmNodeType0Name'))]", + "location": "[parameters('computeLocation')]", + "dependsOn": [ + "[concat('Microsoft.Network/publicIPAddresses/',concat(variables('lbIPName'),'-','0'))]" + ], + "properties": { + "frontendIPConfigurations": [ + { + "name": "LoadBalancerIPConfig", + "properties": { + "publicIPAddress": { + "id": "[resourceId('Microsoft.Network/publicIPAddresses',concat(variables('lbIPName'),'-','0'))]" + } + } + } + ], + "backendAddressPools": [ + { + "name": "LoadBalancerBEAddressPool", + "properties": {} + } + ], + "loadBalancingRules": [ + { + "name": "LBRule", + "properties": { + "backendAddressPool": { + "id": "[variables('lbPoolID0')]" + }, + "backendPort": "[variables('nt0fabricTcpGatewayPort')]", + "enableFloatingIP": false, + "frontendIPConfiguration": { + "id": "[variables('lbIPConfig0')]" + }, + "frontendPort": "[variables('nt0fabricTcpGatewayPort')]", + "idleTimeoutInMinutes": 5, + "probe": { + "id": "[variables('lbProbeID0')]" + }, + "protocol": "Tcp" + } + }, + { + "name": "LBHttpRule", + "properties": { + "backendAddressPool": { + "id": "[variables('lbPoolID0')]" + }, + "backendPort": "[variables('nt0fabricHttpGatewayPort')]", + "enableFloatingIP": false, + "frontendIPConfiguration": { + "id": "[variables('lbIPConfig0')]" + }, + "frontendPort": "[variables('nt0fabricHttpGatewayPort')]", + "idleTimeoutInMinutes": 5, + "probe": { + "id": "[variables('lbHttpProbeID0')]" + }, + "protocol": "Tcp" + } + } + ], + "probes": [ + { + "name": "FabricGatewayProbe", + "properties": { + "intervalInSeconds": 5, + "numberOfProbes": 2, + "port": "[variables('nt0fabricTcpGatewayPort')]", + "protocol": "Tcp" + } + }, + { + "name": "FabricHttpGatewayProbe", + "properties": { + "intervalInSeconds": 5, + "numberOfProbes": 2, + "port": "[variables('nt0fabricHttpGatewayPort')]", + "protocol": "Tcp" + } + } + ], + "inboundNatPools": [ + { + "name": "LoadBalancerBEAddressNatPool", + "properties": { + "backendPort": 3389, + "frontendIPConfiguration": { + "id": "[variables('lbIPConfig0')]" + }, + "frontendPortRangeEnd": 4500, + "frontendPortRangeStart": 3389, + "protocol": "Tcp" + } + } + ] + } + }, + { + "apiVersion": "2019-06-01", + "type": "Microsoft.Storage/storageAccounts", + "name": "[variables('uniqueStringArray0')[copyIndex()]]", + "location": "[parameters('computeLocation')]", + "properties": { + "accountType": "[variables('storageAccountType')]" + }, + "copy": { + "name": "storageLoop", + "count": 5 + }, + "sku": { + "name": "Standard_LRS" + }, + "kind": "Storage" + }, + { + "apiVersion": "2019-12-01", + "type": "Microsoft.Compute/virtualMachineScaleSets", + "name": "[parameters('vmNodeType0Name')]", + "location": "[parameters('computeLocation')]", + "dependsOn": [ + "[concat('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]", + "[concat('Microsoft.Storage/storageAccounts/', variables('uniqueStringArray0')[0])]", + "[concat('Microsoft.Storage/storageAccounts/', variables('uniqueStringArray0')[1])]", + "[concat('Microsoft.Storage/storageAccounts/', variables('uniqueStringArray0')[2])]", + "[concat('Microsoft.Storage/storageAccounts/', variables('uniqueStringArray0')[3])]", + "[concat('Microsoft.Storage/storageAccounts/', variables('uniqueStringArray0')[4])]", + "[concat('Microsoft.Network/loadBalancers/', concat('LB','-', parameters('clusterName'),'-',parameters('vmNodeType0Name')))]", + "[concat('Microsoft.Storage/storageAccounts/', variables('supportLogStorageAccountName'))]", + "[concat('Microsoft.Storage/storageAccounts/', variables('applicationDiagnosticsStorageAccountName'))]" + ], + "properties": { + "overprovision": "[parameters('overProvision')]", + "upgradePolicy": { + "mode": "Automatic" + }, + "virtualMachineProfile": { + "extensionProfile": { + "extensions": [ + { + "name": "[concat(parameters('vmNodeType0Name'),'_ServiceFabricNode')]", + "properties": { + "type": "ServiceFabricNode", + "autoUpgradeMinorVersion": true, + "protectedSettings": { + "StorageAccountKey1": "[listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('supportLogStorageAccountName')),'2015-06-15').key1]", + "StorageAccountKey2": "[listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('supportLogStorageAccountName')),'2015-06-15').key2]" + }, + "publisher": "Microsoft.Azure.ServiceFabric", + "settings": { + "clusterEndpoint": "[reference(parameters('clusterName')).clusterEndpoint]", + "nodeTypeRef": "[parameters('vmNodeType0Name')]", + "dataPath": "D:\\\\SvcFab", + "durabilityLevel": "Bronze", + "enableParallelJobs": true + }, + "typeHandlerVersion": "1.0" + } + }, + { + "name": "[concat(parameters('vmNodeType0Name'),'OMS')]", + "properties": { + "publisher": "Microsoft.EnterpriseCloud.Monitoring", + "type": "MicrosoftMonitoringAgent", + "typeHandlerVersion": "1.0", + "autoUpgradeMinorVersion": true, + "settings": { + "workspaceId": "[reference(resourceId('Microsoft.OperationalInsights/workspaces/', variables('omsWorkspacename')), '2015-11-01-preview').customerId]" + }, + "protectedSettings": { + "workspaceKey": "[listKeys(resourceId('Microsoft.OperationalInsights/workspaces/', variables('omsWorkspacename')),'2015-11-01-preview').primarySharedKey]" + } + } + }, + { + "name": "[concat('VMDiagnosticsVmExt','_vmNodeType0Name')]", + "properties": { + "type": "IaaSDiagnostics", + "autoUpgradeMinorVersion": true, + "protectedSettings": { + "storageAccountName": "[variables('applicationDiagnosticsStorageAccountName')]", + "storageAccountKey": "[listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('applicationDiagnosticsStorageAccountName')),'2015-06-15').key1]", + "storageAccountEndPoint": "https://core.windows.net/" + }, + "publisher": "Microsoft.Azure.Diagnostics", + "settings": { + "WadCfg": { + "DiagnosticMonitorConfiguration": { + "overallQuotaInMB": "50000", + "EtwProviders": { + "EtwEventSourceProviderConfiguration": [ + { + "provider": "Microsoft-ServiceFabric-Actors", + "scheduledTransferKeywordFilter": "1", + "scheduledTransferPeriod": "PT5M", + "DefaultEvents": { + "eventDestination": "ServiceFabricReliableActorEventTable" + } + }, + { + "provider": "Microsoft-ServiceFabric-Services", + "scheduledTransferPeriod": "PT5M", + "DefaultEvents": { + "eventDestination": "ServiceFabricReliableServiceEventTable" + } + } + ], + "EtwManifestProviderConfiguration": [ + { + "provider": "cbd93bc2-71e5-4566-b3a7-595d8eeca6e8", + "scheduledTransferLogLevelFilter": "Information", + "scheduledTransferKeywordFilter": "4611686018427387904", + "scheduledTransferPeriod": "PT5M", + "DefaultEvents": { + "eventDestination": "ServiceFabricSystemEventTable" + } + } + ] + } + } + }, + "StorageAccount": "[variables('applicationDiagnosticsStorageAccountName')]" + }, + "typeHandlerVersion": "1.5" + } + } + ] + }, + "networkProfile": { + "networkInterfaceConfigurations": [ + { + "name": "[concat(variables('nicName'), '-0')]", + "properties": { + "ipConfigurations": [ + { + "name": "[concat(variables('nicName'),'-',0)]", + "properties": { + "loadBalancerBackendAddressPools": [ + { + "id": "[variables('lbPoolID0')]" + } + ], + "loadBalancerInboundNatPools": [ + { + "id": "[variables('lbNatPoolID0')]" + } + ], + "subnet": { + "id": "[variables('subnet0Ref')]" + } + } + } + ], + "primary": true + } + } + ] + }, + "osProfile": { + "adminPassword": "[parameters('adminPassword')]", + "adminUsername": "[parameters('adminUsername')]", + "computernamePrefix": "[parameters('vmNodeType0Name')]" + }, + "storageProfile": { + "imageReference": { + "publisher": "[variables('vmImagePublisher')]", + "offer": "[variables('vmImageOffer')]", + "sku": "[variables('vmImageSku')]", + "version": "[variables('vmImageVersion')]" + }, + "osDisk": { + "managedDisk": { + "storageAccountType": "[variables('storageAccountType')]" + }, + "caching": "ReadOnly", + "createOption": "FromImage" + } + } + } + }, + "sku": { + "name": "[variables('vmNodeType0Size')]", + "capacity": 5, + "tier": "Standard" + } + }, + { + "apiVersion": "2019-03-01", + "type": "Microsoft.ServiceFabric/clusters", + "name": "[parameters('clusterName')]", + "location": "[parameters('computeLocation')]", + "dependsOn": [ + "[concat('Microsoft.Storage/storageAccounts/', variables('supportLogStorageAccountName'))]" + ], + "properties": { + "clientCertificateCommonNames": [], + "clientCertificateThumbprints": [], + "clusterState": "Default", + "diagnosticsStorageAccountConfig": { + "blobEndpoint": "[concat('https://',variables('supportLogStorageAccountName'),'.blob.core.windows.net/')]", + "protectedAccountKeyName": "StorageAccountKey1", + "queueEndpoint": "[concat('https://',variables('supportLogStorageAccountName'),'.queue.core.windows.net/')]", + "storageAccountName": "[variables('supportLogStorageAccountName')]", + "tableEndpoint": "[concat('https://',variables('supportLogStorageAccountName'),'.table.core.windows.net/')]" + }, + "fabricSettings": [], + "managementEndpoint": "[concat('http://',reference(concat(variables('lbIPName'),'-','0')).dnsSettings.fqdn,':',variables('nt0fabricHttpGatewayPort'))]", + "nodeTypes": [ + { + "name": "[parameters('vmNodeType0Name')]", + "applicationPorts": { + "endPort": "[variables('nt0applicationEndPort')]", + "startPort": "[variables('nt0applicationStartPort')]" + }, + "clientConnectionEndpointPort": "[variables('nt0fabricTcpGatewayPort')]", + "durabilityLevel": "Bronze", + "ephemeralPorts": { + "endPort": "[variables('nt0ephemeralEndPort')]", + "startPort": "[variables('nt0ephemeralStartPort')]" + }, + "httpGatewayEndpointPort": "[variables('nt0fabricHttpGatewayPort')]", + "isPrimary": true, + "vmInstanceCount": 5 + } + ], + "provisioningState": "Default", + "reliabilityLevel": "Silver", + "vmImage": "Windows" + } + }, + { + "apiVersion": "2015-11-01-preview", + "location": "[variables('omsWorkspaceLocation')]", + "name": "[variables('omsWorkspacename')]", + "type": "Microsoft.OperationalInsights/workspaces", + "properties": { + "sku": { + "name": "Standard" + } + }, + "resources": [ + { + "condition": "[equals(parameters('enablePremiumManagement'), 'Yes')]", + "apiVersion": "2015-11-01-preview", + "name": "[concat(variables('applicationDiagnosticsStorageAccountName'),variables('omsWorkspacename'))]", + "type": "storageInsightConfigs", + "dependsOn": [ + "[concat('Microsoft.OperationalInsights/workspaces/', variables('omsWorkspacename'))]", + "[concat('Microsoft.Storage/storageAccounts/', variables('applicationDiagnosticsStorageAccountName'))]" + ], + "properties": { + "containers": [], + "tables": [ + "WADServiceFabric*EventTable", + "WADWindowsEventLogsTable", + "WADETWEventTable" + ], + "storageAccount": { + "id": "[resourceId('Microsoft.Storage/storageaccounts/', variables('applicationDiagnosticsStorageAccountName'))]", + "key": "[listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('applicationDiagnosticsStorageAccountName')),'2015-06-15').key1]" + } + } + }, + { + "condition": "[equals(parameters('enablePremiumManagement'), 'Yes')]", + "apiVersion": "2015-11-01-preview", + "type": "datasources", + "name": "LogicalDisk1", + "dependsOn": [ + "[concat('Microsoft.OperationalInsights/workspaces/', variables('omsWorkspaceName'))]" + ], + "kind": "WindowsPerformanceCounter", + "properties": { + "objectName": "LogicalDisk", + "instanceName": "*", + "intervalSeconds": 10, + "counterName": "Avg Disk sec/Read" + } + }, + { + "condition": "[equals(parameters('enablePremiumManagement'), 'Yes')]", + "apiVersion": "2015-11-01-preview", + "type": "datasources", + "name": "LogicalDisk2", + "dependsOn": [ + "[concat('Microsoft.OperationalInsights/workspaces/', variables('omsWorkspaceName'))]" + ], + "kind": "WindowsPerformanceCounter", + "properties": { + "objectName": "LogicalDisk", + "instanceName": "*", + "intervalSeconds": 10, + "counterName": "Avg Disk sec/Write" + } + }, + { + "condition": "[equals(parameters('enablePremiumManagement'), 'Yes')]", + "apiVersion": "2015-11-01-preview", + "type": "datasources", + "name": "LogicalDisk3", + "dependsOn": [ + "[concat('Microsoft.OperationalInsights/workspaces/', variables('omsWorkspaceName'))]" + ], + "kind": "WindowsPerformanceCounter", + "properties": { + "objectName": "LogicalDisk", + "instanceName": "*", + "intervalSeconds": 10, + "counterName": "Current Disk Queue Lenght" + } + }, + { + "condition": "[equals(parameters('enablePremiumManagement'), 'Yes')]", + "apiVersion": "2015-11-01-preview", + "type": "datasources", + "name": "LogicalDisk4", + "dependsOn": [ + "[concat('Microsoft.OperationalInsights/workspaces/', variables('omsWorkspaceName'))]" + ], + "kind": "WindowsPerformanceCounter", + "properties": { + "objectName": "LogicalDisk", + "instanceName": "*", + "intervalSeconds": 10, + "counterName": "Disk Reads/sec" + } + }, + { + "condition": "[equals(parameters('enablePremiumManagement'), 'Yes')]", + "apiVersion": "2015-11-01-preview", + "type": "datasources", + "name": "LogicalDisk5", + "dependsOn": [ + "[concat('Microsoft.OperationalInsights/workspaces/', variables('omsWorkspaceName'))]" + ], + "kind": "WindowsPerformanceCounter", + "properties": { + "objectName": "LogicalDisk", + "instanceName": "*", + "intervalSeconds": 10, + "counterName": "Disk Transfers/sec" + } + }, + { + "condition": "[equals(parameters('enablePremiumManagement'), 'Yes')]", + "apiVersion": "2015-11-01-preview", + "type": "datasources", + "name": "LogicalDisk6", + "dependsOn": [ + "[concat('Microsoft.OperationalInsights/workspaces/', variables('omsWorkspaceName'))]" + ], + "kind": "WindowsPerformanceCounter", + "properties": { + "objectName": "LogicalDisk", + "instanceName": "*", + "intervalSeconds": 10, + "counterName": "Disk Writes/sec" + } + }, + { + "condition": "[equals(parameters('enablePremiumManagement'), 'Yes')]", + "apiVersion": "2015-11-01-preview", + "type": "datasources", + "name": "LogicalDisk7", + "dependsOn": [ + "[concat('Microsoft.OperationalInsights/workspaces/', variables('omsWorkspaceName'))]" + ], + "kind": "WindowsPerformanceCounter", + "properties": { + "objectName": "LogicalDisk", + "instanceName": "*", + "intervalSeconds": 10, + "counterName": "Free Megabytes" + } + }, + { + "condition": "[equals(parameters('enablePremiumManagement'), 'Yes')]", + "apiVersion": "2015-11-01-preview", + "type": "datasources", + "name": "LogicalDisk8", + "dependsOn": [ + "[concat('Microsoft.OperationalInsights/workspaces/', variables('omsWorkspaceName'))]" + ], + "kind": "WindowsPerformanceCounter", + "properties": { + "objectName": "LogicalDisk", + "instanceName": "*", + "intervalSeconds": 10, + "counterName": "% Free Space" + } + }, + { + "condition": "[equals(parameters('enablePremiumManagement'), 'Yes')]", + "apiVersion": "2015-11-01-preview", + "type": "datasources", + "name": "Memory1", + "dependsOn": [ + "[concat('Microsoft.OperationalInsights/workspaces/', variables('omsWorkspaceName'))]" + ], + "kind": "WindowsPerformanceCounter", + "properties": { + "objectName": "Memory", + "instanceName": "*", + "intervalSeconds": 10, + "counterName": "Available MBytes" + } + }, + { + "condition": "[equals(parameters('enablePremiumManagement'), 'Yes')]", + "apiVersion": "2015-11-01-preview", + "type": "datasources", + "name": "Memory2", + "dependsOn": [ + "[concat('Microsoft.OperationalInsights/workspaces/', variables('omsWorkspaceName'))]" + ], + "kind": "WindowsPerformanceCounter", + "properties": { + "objectName": "Memory", + "instanceName": "*", + "intervalSeconds": 10, + "counterName": "% Committed Bytes In Use" + } + }, + { + "condition": "[equals(parameters('enablePremiumManagement'), 'Yes')]", + "apiVersion": "2015-11-01-preview", + "type": "datasources", + "name": "Network1", + "dependsOn": [ + "[concat('Microsoft.OperationalInsights/workspaces/', variables('omsWorkspaceName'))]" + ], + "kind": "WindowsPerformanceCounter", + "properties": { + "objectName": "Network Adapter", + "instanceName": "*", + "intervalSeconds": 10, + "counterName": "Bytes Received/sec" + } + }, + { + "condition": "[equals(parameters('enablePremiumManagement'), 'Yes')]", + "apiVersion": "2015-11-01-preview", + "type": "datasources", + "name": "Network2", + "dependsOn": [ + "[concat('Microsoft.OperationalInsights/workspaces/', variables('omsWorkspaceName'))]" + ], + "kind": "WindowsPerformanceCounter", + "properties": { + "objectName": "Network Adapter", + "instanceName": "*", + "intervalSeconds": 10, + "counterName": "Bytes Sent/sec" + } + }, + { + "condition": "[equals(parameters('enablePremiumManagement'), 'Yes')]", + "apiVersion": "2015-11-01-preview", + "type": "datasources", + "name": "Network3", + "dependsOn": [ + "[concat('Microsoft.OperationalInsights/workspaces/', variables('omsWorkspaceName'))]" + ], + "kind": "WindowsPerformanceCounter", + "properties": { + "objectName": "Network Adapter", + "instanceName": "*", + "intervalSeconds": 10, + "counterName": "Bytes Total/sec" + } + }, + { + "condition": "[equals(parameters('enablePremiumManagement'), 'Yes')]", + "apiVersion": "2015-11-01-preview", + "type": "datasources", + "name": "CPU1", + "dependsOn": [ + "[concat('Microsoft.OperationalInsights/workspaces/', variables('omsWorkspaceName'))]" + ], + "kind": "WindowsPerformanceCounter", + "properties": { + "objectName": "Processor", + "instanceName": "_Total", + "intervalSeconds": 10, + "counterName": "% Processor Time" + } + }, + { + "condition": "[equals(parameters('enablePremiumManagement'), 'Yes')]", + "apiVersion": "2015-11-01-preview", + "type": "datasources", + "name": "CPU2", + "dependsOn": [ + "[concat('Microsoft.OperationalInsights/workspaces/', variables('omsWorkspaceName'))]" + ], + "kind": "WindowsPerformanceCounter", + "properties": { + "objectName": "System", + "instanceName": "*", + "intervalSeconds": 10, + "counterName": "Processor Queue Lenght" + } + }, + { + "condition": "[equals(parameters('enablePremiumManagement'), 'Yes')]", + "apiVersion": "2015-11-01-preview", + "type": "datasources", + "name": "System", + "dependsOn": [ + "[concat('Microsoft.OperationalInsights/workspaces/', variables('omsWorkspaceName'))]" + ], + "kind": "WindowsEvent", + "properties": { + "eventLogName": "System", + "eventTypes": [ + { + "eventType": "Error" + }, + { + "eventType": "Warning" + } + ] + } + }, + { + "condition": "[equals(parameters('enablePremiumManagement'), 'Yes')]", + "apiVersion": "2015-11-01-preview", + "type": "datasources", + "name": "Application", + "dependsOn": [ + "[concat('Microsoft.OperationalInsights/workspaces/', variables('omsWorkspaceName'))]" + ], + "kind": "WindowsEvent", + "properties": { + "eventLogName": "Application", + "eventTypes": [ + { + "eventType": "Error" + }, + { + "eventType": "Warning" + } + ] + } + }, + { + "condition": "[equals(parameters('enablePremiumManagement'), 'Yes')]", + "apiVersion": "2015-11-01-preview", + "type": "datasources", + "name": "IISLog", + "dependsOn": [ + "[concat('Microsoft.OperationalInsights/workspaces/', variables('omsWorkspaceName'))]" + ], + "kind": "IISLogs", + "properties": { + "state": "OnPremiseEnabled" + } + } + ] + }, + { + "condition": "[equals(parameters('enablePremiumManagement'), 'Yes')]", + "apiVersion": "2015-11-01-preview", + "location": "[variables('omsWorkspaceLocation')]", + "name": "[concat(variables('omsSolution').batch[copyIndex()].solutionName)]", + "type": "Microsoft.OperationsManagement/solutions", + "dependsOn": [ + "[concat('Microsoft.OperationalInsights/workspaces/', variables('OMSWorkspacename'))]", + "[concat('Microsoft.ServiceFabric/clusters/', parameters('clusterName'))]" + ], + "copy": { + "name": "solutionCopy", + "count": "[length(variables('omsSolution').batch)]" + }, + "properties": { + "workspaceResourceId": "[resourceId('Microsoft.OperationalInsights/workspaces/', variables('omsWorkspacename'))]" + }, + "plan": { + "name": "[variables('omsSolution').batch[copyIndex()].solutionName]", + "publisher": "Microsoft", + "product": "[Concat('OMSGallery/', variables('omsSolution').batch[copyIndex()].solution)]", + "promotionCode": "" + } + } + ], + "outputs": { + "clusterMgmtEndpoint": { + "value": "[concat(parameters('dnsName'), '.', parameters('computeLocation'), '.cloudapp.azure.com:19080/explorer')]", + "type": "string" + }, + "reliabilityLevel": { + "value": "[reference(parameters('clusterName')).reliabilityLevel]", + "type": "string" + }, + "platform": { + "value": "[reference(parameters('clusterName')).vmImage]", + "type": "string" + } + } +} \ No newline at end of file diff --git a/Managed Application Sample Packages/201-managed-service-fabric/managedservicefabric.zip b/Managed Application Sample Packages/201-managed-service-fabric/managedservicefabric.zip new file mode 100644 index 0000000..09b02e6 Binary files /dev/null and b/Managed Application Sample Packages/201-managed-service-fabric/managedservicefabric.zip differ diff --git a/Managed Application Sample Packages/201-managed-service-fabric/output.json b/Managed Application Sample Packages/201-managed-service-fabric/output.json new file mode 100644 index 0000000..7746a64 --- /dev/null +++ b/Managed Application Sample Packages/201-managed-service-fabric/output.json @@ -0,0 +1,15 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": {}, + "variables": { + "oms": "[concat(take(resourceGroup().name, 5), '-', 'OMS')]" + }, + "resources": [], + "outputs": { + "oms": { + "type": "string", + "value": "[variables('oms')]" + } + } +} \ No newline at end of file diff --git a/Managed Application Sample Packages/201-managed-service-fabric/readme.md b/Managed Application Sample Packages/201-managed-service-fabric/readme.md new file mode 100644 index 0000000..aa114b3 --- /dev/null +++ b/Managed Application Sample Packages/201-managed-service-fabric/readme.md @@ -0,0 +1,51 @@ +# Managed Service Fabric with Azure management services + +>Note: This sample is for Managed Application in Service Catalog. For Marketplace, please see these instructions: +[**Marketplace Managed Application**](https://docs.microsoft.com/en-us/azure/managed-applications/publish-marketplace-app) + +## Deploy this sample to your Service Catalog + +### Deploy using Azure portal + +Clicking on the button below, will create the Managed Application definition to a Resource Group in your Azure subscription. + +[![Deploy to Azure](http://azuredeploy.net/deploybutton.png)](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2Fazure%2Fazure-managedapp-samples%2Fmaster%2FManaged%2520Application%2520Sample%2520Packages%2F201-managed-service-fabric%2Fazuredeploy.json) + +### Deploy using PowerShell + +Modify the snippet below to deploy Managed Application definition to a Resource Group in your Azure subscription + +````powershell +$rgname = "" +$location = "" +$authorization = ":" +$uri = "https://raw.githubusercontent.com/Azure/azure-managedapp-samples/master/Managed Application Sample Packages/201-managed-service-fabric/managedservicefabric.zip" + +New-AzureRmManagedApplicationDefinition -Name "ManagedServiceFabric" ` + -ResourceGroupName $rgname ` + -DisplayName "Managed Service Fabric" ` + -Description "Managed Service Fabric with Azure mgmt." ` + -Location $location ` + -LockLevel ReadOnly ` + -PackageFileUri $uri ` + -Authorization $authorization ` + -Verbose +```` + +### Deploy using AzureCLI + +Modify the snippet below to deploy Managed Application definition to a Resource Group in your Azure subscription + +````azureCLI +az managedapp definition create \ + --name "ManagedServiceFabric" \ + --location \ + --resource-group \ + --lock-level ReadOnly \ + --display-name "Managed Service Fabric" \ + --description "Managed Service Fabric with Azure mgmt." \ + --authorizations ":" \ + --package-file-uri "https://raw.githubusercontent.com/Azure/azure-managedapp-samples/master/Managed Application Sample Packages/201-managed-service-fabric/managedservicefabric.zip" +```` + +![alt text](images/appliance.png "Azure Managed Application") \ No newline at end of file diff --git a/Managed Application Sample Packages/201-managed-sql-iaas/README.md b/Managed Application Sample Packages/201-managed-sql-iaas/README.md new file mode 100644 index 0000000..f39d357 --- /dev/null +++ b/Managed Application Sample Packages/201-managed-sql-iaas/README.md @@ -0,0 +1,49 @@ +# Managed SQL 2017 IaaS with automated patching and backup + +>Note: This sample is for Managed Application in Service Catalog. For Marketplace, please see these instructions: +[**Marketplace Managed Application**](https://docs.microsoft.com/en-us/azure/managed-applications/publish-marketplace-app) + +## Deploy this sample to your Service Catalog + +### Deploy using Azure Portal + +Clicking on the button below, will create the Managed Application definition to a Resource Group in your Azure subscription. + +[![Deploy to Azure](http://azuredeploy.net/deploybutton.png)](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2Fazure%2Fazure-managedapp-samples%2Fmaster%2FManaged%2520Application%2520Sample%2520Packages%2F201-managed-sql-iaas%2Fazuredeploy.json) + +### Deploy using PowerShell + +Modify the snippet below to deploy Managed Application definition to a Resource Group in your Azure subscription + +````powershell +$rgname = "" +$location = "" +$authorization = ":" +$uri = "https://raw.githubusercontent.com/Azure/azure-managedapp-samples/master/Managed Application Sample Packages/201-managed-sql-iaas/managedSql.zip" + +New-AzureRmManagedApplicationDefinition -Name "ManagedSql" ` + -ResourceGroupName $rgname ` + -DisplayName "Managed SQL IaaS" ` + -Description "Managed SQL IaaS with automated patching and backup" ` + -Location $location ` + -LockLevel ReadOnly ` + -PackageFileUri $uri ` + -Authorization $authorization ` + -Verbose +```` + +### Deploy using AzureCLI + +Modify the snippet below to deploy Managed Application definition to a Resource Group in your Azure subscription + +````azureCLI +az managedapp definition create \ + --name "ManagedSql" \ + --location \ + --resource-group \ + --lock-level ReadOnly \ + --display-name "Managed SQL IaaS" \ + --description "Managed SQL IaaS with automated patching and backup" \ + --authorizations ":" \ + --package-file-uri "https://raw.githubusercontent.com/Azure/azure-managedapp-samples/master/Managed Application Sample Packages/201-managed-sql-iaas/managedSql.zip" +```` \ No newline at end of file diff --git a/Managed Application Sample Packages/201-managed-sql-iaas/azuredeploy.json b/Managed Application Sample Packages/201-managed-sql-iaas/azuredeploy.json new file mode 100644 index 0000000..5e85a4b --- /dev/null +++ b/Managed Application Sample Packages/201-managed-sql-iaas/azuredeploy.json @@ -0,0 +1,82 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "type": "string", + "defaultValue": "ManagedSqlIaaS", + "metadata": { + "description": "Provide a name for the managed application" + } + }, + "location": { + "type": "string", + "metadata": { + "description": "Specify the Azure region to place the application definition" + } + }, + "lockLevel": { + "type": "string", + "allowedValues": [ + "ReadOnly", + "CanNotDelete" + ], + "defaultValue": "ReadOnly", + "metadata": { + "description": "Specify the resource lock being used for the managed application" + } + }, + "authorizations": { + "type": "array", + "metadata": { + "description": "Provide the authorization mapping for the managed application." + } + }, + "description": { + "type": "string", + "defaultValue": "Managed SQL IaaS", + "metadata": { + "description": "Provide a brief description of the managed application" + } + }, + "displayName": { + "type": "string", + "defaultValue": "Managed SQL Application", + "metadata": { + "description": "Display name for the managed application" + } + } + }, + "variables": { + "packageFileUri": "[uri(deployment().properties.templateLink.uri, 'managedSql.zip')]" + }, + "resources": [ + { + "apiVersion": "2017-09-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "type": "Microsoft.Solutions/applicationDefinitions", + "properties": { + "lockLevel": "[parameters('lockLevel')]", + "authorizations": "[array(parameters('authorizations'))]", + "description": "[parameters('description')]", + "displayName": "[parameters('displayName')]", + "packageFileUri": "[variables('packageFileUri')]" + } + } + ], + "outputs": { + "managedApplicationName": { + "type": "string", + "value": "[parameters('name')]" + }, + "lockLevel": { + "type": "string", + "value": "[parameters('locklevel')]" + }, + "packageFileUri": { + "type": "string", + "value": "[variables('packageFileUri')]" + } + } +} \ No newline at end of file diff --git a/Managed Application Sample Packages/201-managed-sql-iaas/createUiDefinition.json b/Managed Application Sample Packages/201-managed-sql-iaas/createUiDefinition.json new file mode 100644 index 0000000..c055376 --- /dev/null +++ b/Managed Application Sample Packages/201-managed-sql-iaas/createUiDefinition.json @@ -0,0 +1,169 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/0.1.2-preview/CreateUIDefinition.MultiVm.json#", + "handler": "Microsoft.Azure.CreateUIDef", + "version": "0.1.2-preview", + "parameters": { + "basics": [ + {} + ], + "steps": [ + { + "name": "credentialsConfig", + "label": "SQL VM Credential", + "subLabel": { + "preValidation": "Configure the SQL VM credentials", + "postValidation": "Done" + }, + "bladeTitle": "Credential", + "elements": [ + { + "name": "adminUsername", + "type": "Microsoft.Compute.UserNameTextBox", + "label": "User name", + "toolTip": "Admin username for the virtual machine", + "osPlatform": "Windows", + "constraints": { + "required": true + } + }, + { + "name": "adminPassword", + "type": "Microsoft.Compute.CredentialsCombo", + "label": { + "password": "Password", + "confirmPassword": "Confirm password" + }, + "toolTip": { + "password": "Admin password for the virtual machine" + }, + "osPlatform": "Windows", + "constraints": { + "required": true + } + } + ] + }, + { + "name": "vmConfig", + "label": "SQL VM settings", + "subLabel": { + "preValidation": "Configure the SQL virtual machine settings", + "postValidation": "Done" + }, + "bladeTitle": "SQL VM Settings", + "elements": [ + { + "name": "vmNamePrefix", + "type": "Microsoft.Common.TextBox", + "label": "Virtual Machine Name", + "toolTip": "Provide a name for the virtual machine", + "defaultValue": "", + "constraints": { + "required": true, + "regex": "[a-z][a-z0-9-]{2,5}[a-z0-9]$", + "validationMessage": "Must be 3-5 characters." + } + }, + { + "name": "vmSize", + "type": "Microsoft.Compute.SizeSelector", + "label": "Virtual machine size", + "toolTip": "Select the virtual machine size", + "recommendedSizes": [ + "Standard_DS12_v2" + ], + "constraints": { + "allowedSizes": [ + "Standard_D1_v2", + "Standard_DS12_v2" + ] + }, + "osPlatform": "Windows", + "count": 1 + } + ] + }, + { + "name": "sqlConfig", + "label": "SQL settings", + "subLabel": { + "preValidation": "Configure the SQL connectivity endpoint", + "postValidation": "Done" + }, + "bladeTitle": "SQL endpoint settings", + "elements": [ + { + "name": "dnsAndPublicIP", + "type": "Microsoft.Network.PublicIpAddressCombo", + "label": { + "publicIpAddress": "Public IP address", + "domainNameLabel": "DNS label" + }, + "toolTip": { + "domainNameLabel": "DNS endpoint for the Managed SQL VM IP address." + }, + "defaultValue": { + "publicIpAddressName": "ip01" + }, + "options": { + "hideNone": true, + "hideDomainNameLabel": false + }, + "constraints": { + "required": { + "domainNameLabel": true + } + } + }, + { + "name": "virtualNetwork", + "type": "Microsoft.Network.VirtualNetworkCombo", + "label": { + "virtualNetwork": "Virtual network", + "subnets": "Subnets" + }, + "toolTip": { + "virtualNetwork": "Virtual Network Name", + "subnets": "Subnet requried for SQL VM" + }, + "defaultValue": { + "name": "sql-vnet", + "addressPrefixSize": "/22" + }, + "constraints": { + "minAddressPrefixSize": "/22" + }, + "subnets": { + "subnet1": { + "label": "Subnet name", + "defaultValue": { + "name": "sql-subnet", + "addressPrefixSize": "/24" + }, + "constraints": { + "minAddressPrefixSize": "/24", + "minAddressCount": 12, + "requireContiguousAddresses": false + } + } + } + } + ] + } + ], + "outputs": { + "location": "[location()]", + "virtualMachineSize": "[steps('vmConfig').vmSize]", + "virtualMachineName": "[steps('vmConfig').vmNamePrefix]", + "adminUsername": "[steps('credentialsConfig').adminUsername]", + "adminPassword": "[steps('credentialsConfig').adminPassword.password]", + "dnsName": "[steps('sqlConfig').dnsAndPublicIP.domainNameLabel]", + "publicIPAddressName": "[steps('sqlConfig').dnsAndPublicIP.name]", + "applicationResourceName": "[steps('vmConfig').vmName]", + "subnetName": "[steps('sqlConfig').virtualNetwork.subnets.subnet1.name]", + "subnetPrefix": "[steps('sqlConfig').virtualNetwork.subnets.subnet1.addressPrefix]", + "addressPrefix": "[steps('sqlConfig').virtualNetwork.addressPrefix]", + "virtualNetworkName": "[steps('sqlConfig').virtualNetwork.name]" + } + } +} \ No newline at end of file diff --git a/Managed Application Sample Packages/201-managed-sql-iaas/mainTemplate.json b/Managed Application Sample Packages/201-managed-sql-iaas/mainTemplate.json new file mode 100644 index 0000000..dfb2c88 --- /dev/null +++ b/Managed Application Sample Packages/201-managed-sql-iaas/mainTemplate.json @@ -0,0 +1,297 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "virtualMachineName": { + "type": "string" + }, + "virtualMachineSize": { + "type": "string" + }, + "adminUsername": { + "type": "string" + }, + "virtualNetworkName": { + "type": "string" + }, + "adminPassword": { + "type": "securestring" + }, + "addressPrefix": { + "type": "string" + }, + "subnetName": { + "type": "string" + }, + "subnetPrefix": { + "type": "string" + }, + "publicIpAddressName": { + "type": "string" + }, + "dnsName": { + "type": "string" + } + }, + "variables": { + "sqlPortNumber": 1433, + "networkInterfaceName": "sqlNic", + "networkSecurityGroupName": "sqlNsg", + "publicIpAddresstype": "Dynamic", + "publicIpAddressSku": "Basic", + "sqlConnectivityType": "Private", + "sqlStorageDisksCount": 1, + "sqlStorageWorkloadType": "GENERAL", + "sqlStorageDisksConfigurationType": "NEW", + "sqlStorageStartingDeviceId": 2, + "sqlAutoPatchingDayOfWeek": "Sunday", + "sqlAutopatchingStartHour": 2, + "sqlAutopatchingWindowDuration": 60, + "sqlAutobackupRetentionPeriod": 30, + "sqlAutoBackupStorageAccountName": "[toLower(concat('bak', uniqueString(resourceGroup().id),'2'))]", + "sqlAutobackupStorageAccountType": "Standard_LRS", + "backupSystemDBs": "true", + "backupScheduleType": "Automated", + "rServicesEnabled": "false", + "vnetId": "[resourceId('Microsoft.Network/virtualNetworks', parameters('virtualNetworkName'))]", + "subnetRef": "[concat(variables('vnetId'), '/subnets/', parameters('subnetName'))]" + }, + "resources": [ + { + "name": "[parameters('virtualMachineName')]", + "type": "Microsoft.Compute/virtualMachines", + "apiVersion": "2019-12-01", + "location": "[parameters('location')]", + "dependsOn": [ + "[resourceId('Microsoft.Network/networkInterfaces/', variables('networkInterfaceName'))]" + ], + "properties": { + "osProfile": { + "computerName": "[parameters('virtualMachineName')]", + "adminUsername": "[parameters('adminUsername')]", + "adminPassword": "[parameters('adminPassword')]", + "windowsConfiguration": { + "provisionVmAgent": "true" + } + }, + "hardwareProfile": { + "vmSize": "[parameters('virtualMachineSize')]" + }, + "storageProfile": { + "imageReference": { + "publisher": "MicrosoftSQLServer", + "offer": "SQL2017-WS2016", + "sku": "Enterprise", + "version": "latest" + }, + "osDisk": { + "createOption": "fromImage", + "managedDisk": { + "storageAccountType": "Premium_LRS" + } + }, + "dataDisks": [ + { + "createOption": "empty", + "lun": 0, + "diskSizeGB": "1023", + "caching": "ReadOnly", + "managedDisk": { + "storageAccountType": "Premium_LRS" + } + } + ] + }, + "networkProfile": { + "networkInterfaces": [ + { + "id": "[resourceId('Microsoft.Network/networkInterfaces', variables('networkInterfaceName'))]" + } + ] + } + } + }, + { + "apiVersion": "2019-12-01", + "type": "Microsoft.Compute/virtualMachines/extensions", + "name": "[concat(parameters('virtualMachineName'), '/SqlIaasExtension')]", + "location": "[parameters('location')]", + "dependsOn": [ + "[concat('Microsoft.Compute/virtualMachines/', parameters('virtualMachineName'))]" + ], + "properties": { + "type": "SqlIaaSAgent", + "publisher": "Microsoft.SqlServer.Management", + "typeHandlerVersion": "1.2", + "autoUpgradeMinorVersion": "true", + "settings": { + "AutoTelemetrySettings": { + "Region": "[parameters('location')]" + }, + "AutoPatchingSettings": { + "PatchCategory": "WindowsMandatoryUpdates", + "Enable": true, + "DayOfWeek": "[variables('sqlAutopatchingDayOfWeek')]", + "MaintenanceWindowStartingHour": "[variables('sqlAutopatchingStartHour')]", + "MaintenanceWindowDuration": "[variables('sqlAutopatchingWindowDuration')]" + }, + "AutoBackupSettings": { + "Enable": true, + "RetentionPeriod": "[variables('sqlAutobackupRetentionPeriod')]", + "EnableEncryption": true, + "BackupSystemDbs": "[variables('backupSystemDbs')]", + "BackupScheduleType": "[variables('backupScheduleType')]" + }, + "KeyVaultCredentialSettings": { + "Enable": false, + "CredentialName": "" + }, + "ServerConfigurationsManagementSettings": { + "SQLConnectivityUpdateSettings": { + "ConnectivityType": "[variables('sqlConnectivityType')]", + "Port": "[variables('sqlPortNumber')]" + }, + "SQLWorkloadTypeUpdateSettings": { + "SQLWorkloadType": "[variables('sqlStorageWorkloadType')]" + }, + "SQLStorageUpdateSettings": { + "DiskCount": "[variables('sqlStorageDisksCount')]", + "NumberOfColumns": "[variables('sqlStorageDisksCount')]", + "StartingDeviceID": "[variables('sqlStorageStartingDeviceId')]", + "DiskConfigurationType": "[variables('sqlStorageDisksConfigurationType')]" + }, + "AdditionalFeaturesServerConfigurations": { + "IsRServicesEnabled": "[variables('rServicesEnabled')]" + } + } + }, + "protectedSettings": { + "StorageUrl": "[reference(resourceId('Microsoft.Storage/storageAccounts', variables('sqlAutobackupStorageAccountName')), '2015-06-15').primaryEndpoints['blob']]", + "StorageAccessKey": "[listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('sqlAutobackupStorageAccountName')), '2015-06-15').key1]", + "Password": "[parameters('adminPassword')]" + } + } + }, + { + "name": "[variables('sqlAutobackupStorageAccountName')]", + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2019-06-01", + "location": "[parameters('location')]", + "properties": { + "accountType": "[variables('sqlAutobackupStorageAccountType')]" + }, + "sku": { + "name": "Standard_LRS" + }, + "kind": "Storage" + }, + { + "name": "[parameters('virtualNetworkName')]", + "type": "Microsoft.Network/virtualNetworks", + "apiVersion": "2019-11-01", + "location": "[parameters('location')]", + "properties": { + "addressSpace": { + "addressPrefixes": [ + "[parameters('addressPrefix')]" + ] + }, + "subnets": [ + { + "name": "[parameters('subnetName')]", + "properties": { + "addressPrefix": "[parameters('subnetPrefix')]" + } + } + ] + } + }, + { + "name": "[variables('networkInterfaceName')]", + "type": "Microsoft.Network/networkInterfaces", + "apiVersion": "2019-11-01", + "location": "[parameters('location')]", + "dependsOn": [ + "[concat('Microsoft.Network/virtualNetworks/', parameters('virtualNetworkName'))]", + "[concat('Microsoft.Network/publicIpAddresses/', parameters('publicIpAddressName'))]", + "[concat('Microsoft.Network/networkSecurityGroups/', variables('networkSecurityGroupName'))]" + ], + "properties": { + "ipConfigurations": [ + { + "name": "ipconfig1", + "properties": { + "subnet": { + "id": "[variables('subnetRef')]" + }, + "privateIPAllocationMethod": "Dynamic", + "publicIpAddress": { + "id": "[resourceId('Microsoft.Network/publicIpAddresses', parameters('publicIpAddressName'))]" + } + } + } + ], + "enableAcceleratedNetworking": true, + "networkSecurityGroup": { + "id": "[resourceId('Microsoft.Network/networkSecurityGroups', variables('networkSecurityGroupName'))]" + } + } + }, + { + "name": "[parameters('publicIpAddressName')]", + "type": "Microsoft.Network/publicIpAddresses", + "apiVersion": "2019-11-01", + "location": "[parameters('location')]", + "properties": { + "publicIpAllocationMethod": "[variables('publicIpAddressType')]", + "dnsSettings":{ + "domainNameLabel": "[parameters('dnsName')]" + } + }, + "sku": { + "name": "[variables('publicIpAddressSku')]" + } + }, + { + "name": "[variables('networkSecurityGroupName')]", + "type": "Microsoft.Network/networkSecurityGroups", + "apiVersion": "2019-11-01", + "location": "[parameters('location')]", + "properties": { + "securityRules": [ + { + "name": "default-allow-rdp", + "properties": { + "priority": 1000, + "protocol": "TCP", + "access": "Allow", + "direction": "Inbound", + "sourceAddressPrefix": "*", + "sourcePortRange": "*", + "destinationAddressPrefix": "*", + "destinationPortRange": "3389" + } + } + ] + } + } + ], + "outputs": { + "adminUsername": { + "type": "string", + "value": "[parameters('adminUsername')]" + }, + "sqlEndpoint": { + "type": "string", + "value": "[reference(concat(parameters('publicIPAddressName'))).dnsSettings.fqdn]" + }, + "sqlPort": { + "type": "int", + "value": "[variables('sqlPortNumber')]" + } + } +} \ No newline at end of file diff --git a/Managed Application Sample Packages/201-managed-sql-iaas/managedSql.zip b/Managed Application Sample Packages/201-managed-sql-iaas/managedSql.zip new file mode 100644 index 0000000..cc5197c Binary files /dev/null and b/Managed Application Sample Packages/201-managed-sql-iaas/managedSql.zip differ diff --git a/Managed Application Sample Packages/201-managed-storage-account/azuredeploy.json b/Managed Application Sample Packages/201-managed-storage-account/azuredeploy.json new file mode 100644 index 0000000..49b90a6 --- /dev/null +++ b/Managed Application Sample Packages/201-managed-storage-account/azuredeploy.json @@ -0,0 +1,82 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "type": "string", + "defaultValue": "ManagedStorageAccount", + "metadata": { + "description": "Provide a name for the managed application." + } + }, + "location": { + "type": "string", + "metadata": { + "description": "Specify the Azure region to place the application definition." + } + }, + "lockLevel": { + "type": "string", + "allowedValues": [ + "ReadOnly", + "CanNotDelete" + ], + "defaultValue": "ReadOnly", + "metadata": { + "description": "Specify the resource lock being used for the managed application" + } + }, + "authorizations": { + "type": "array", + "metadata": { + "description": "Provide the authorization mapping for the managed application." + } + }, + "description": { + "type": "string", + "defaultValue": "Managed Azure Storage Account", + "metadata": { + "description": "Provide a brief description of the managed application" + } + }, + "displayName": { + "type": "string", + "defaultValue": "Managed Storage Account", + "metadata": { + "description": "Display name for the managed application" + } + } + }, + "variables": { + "packageFileUri": "[uri(deployment().properties.templateLink.uri, 'managedstorage.zip')]" + }, + "resources": [ + { + "apiVersion": "2017-09-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "type": "Microsoft.Solutions/applicationDefinitions", + "properties": { + "lockLevel": "[parameters('lockLevel')]", + "authorizations": "[array(parameters('authorizations'))]", + "description": "[parameters('description')]", + "displayName": "[parameters('displayName')]", + "packageFileUri": "[variables('packageFileUri')]" + } + } + ], + "outputs": { + "managedApplicationName": { + "type": "string", + "value": "[parameters('name')]" + }, + "lockLevel": { + "type": "string", + "value": "[parameters('locklevel')]" + }, + "packageFileUri": { + "type": "string", + "value": "[variables('packageFileUri')]" + } + } +} \ No newline at end of file diff --git a/Managed Application Sample Packages/201-managed-storage-account/createUiDefinition.json b/Managed Application Sample Packages/201-managed-storage-account/createUiDefinition.json new file mode 100644 index 0000000..09af531 --- /dev/null +++ b/Managed Application Sample Packages/201-managed-storage-account/createUiDefinition.json @@ -0,0 +1,46 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/0.1.2-preview/CreateUIDefinition.MultiVm.json#", + "handler": "Microsoft.Azure.CreateUIDef", + "version": "0.1.2-preview", + "parameters": { + "basics": [ + {} + ], + "steps": [ + { + "name": "storageConfig", + "label": "Storage settings", + "subLabel": { + "preValidation": "Configure the infrastructure settings", + "postValidation": "Done" + }, + "bladeTitle": "Storage settings", + "elements": [ + { + "name": "storageAccounts", + "type": "Microsoft.Storage.MultiStorageAccountCombo", + "label": { + "prefix": "Storage account name prefix", + "type": "Storage account type" + }, + "defaultValue": { + "type": "Standard_LRS" + }, + "constraints": { + "allowedTypes": [ + "Premium_LRS", + "Standard_LRS", + "Standard_GRS" + ] + } + } + ] + } + ], + "outputs": { + "storageAccountNamePrefix": "[steps('storageConfig').storageAccounts.prefix]", + "storageAccountType": "[steps('storageConfig').storageAccounts.type]", + "location": "[location()]" + } + } +} \ No newline at end of file diff --git a/Managed Application Sample Packages/201-managed-storage-account/images/storage.png b/Managed Application Sample Packages/201-managed-storage-account/images/storage.png new file mode 100644 index 0000000..7c4f6e2 Binary files /dev/null and b/Managed Application Sample Packages/201-managed-storage-account/images/storage.png differ diff --git a/Managed Application Sample Packages/201-managed-storage-account/mainTemplate.json b/Managed Application Sample Packages/201-managed-storage-account/mainTemplate.json new file mode 100644 index 0000000..32b81ab --- /dev/null +++ b/Managed Application Sample Packages/201-managed-storage-account/mainTemplate.json @@ -0,0 +1,50 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "storageAccountNamePrefix": { + "type": "string" + }, + "storageAccountType": { + "type": "string" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + } + }, + "variables": { + "nestedTemplateUri": "[uri(deployment().properties.templateLink.uri, 'nestedtemplates/storageAccount.json')]" + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2019-10-01", + "name": "nestedDeployment", + "properties": { + "mode": "Incremental", + "templateLink": { + "contentVersion": "1.0.0.0", + "uri": "[variables('nestedTemplateUri')]" + }, + "parameters": { + "storageAccountNamePrefix": { + "value": "[parameters('storageAccountNamePrefix')]" + }, + "storageAccountType": { + "value": "[parameters('storageAccountType')]" + }, + "location": { + "value": "[parameters('location')]" + } + } + } + } + ], + "outputs": { + "storageEndpoint": { + "type": "string", + "value": "[reference('nestedDeployment').outputs.storageEndpoint.value]" + } + } +} \ No newline at end of file diff --git a/Managed Application Sample Packages/201-managed-storage-account/managedstorage.zip b/Managed Application Sample Packages/201-managed-storage-account/managedstorage.zip new file mode 100644 index 0000000..004e59f Binary files /dev/null and b/Managed Application Sample Packages/201-managed-storage-account/managedstorage.zip differ diff --git a/Managed Application Sample Packages/201-managed-storage-account/nestedtemplates/storageAccount.json b/Managed Application Sample Packages/201-managed-storage-account/nestedtemplates/storageAccount.json new file mode 100644 index 0000000..70602ca --- /dev/null +++ b/Managed Application Sample Packages/201-managed-storage-account/nestedtemplates/storageAccount.json @@ -0,0 +1,47 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "storageAccountNamePrefix": { + "type": "string", + "metadata": { + "description": "Prefix for the storage account name" + } + }, + "storageAccountType": { + "type": "string", + "metadata": { + "description": "Storage account type" + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Storage account location" + } + } + }, + "variables": { + "storageAccountName": "[concat(parameters('storageAccountNamePrefix'), uniqueString('storage'))]" + }, + "resources": [ + { + "type": "Microsoft.Storage/storageAccounts", + "name": "[variables('storageAccountName')]", + "apiVersion": "2019-06-01", + "location": "[parameters('location')]", + "sku": { + "name": "[parameters('storageAccountType')]" + }, + "kind": "Storage", + "properties": {} + } + ], + "outputs": { + "storageEndpoint": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Storage/storageAccounts/', variables('storageAccountName')), '2016-01-01').primaryEndpoints.blob]" + } + } +} \ No newline at end of file diff --git a/Managed Application Sample Packages/201-managed-storage-account/readme.md b/Managed Application Sample Packages/201-managed-storage-account/readme.md new file mode 100644 index 0000000..18d2978 --- /dev/null +++ b/Managed Application Sample Packages/201-managed-storage-account/readme.md @@ -0,0 +1,49 @@ +# Managed Azure Storage Account + +>Note: This sample is for Managed Application in Service Catalog. For Marketplace, please see these instructions: +[**Marketplace Managed Application**](https://docs.microsoft.com/en-us/azure/managed-applications/publish-marketplace-app) + +## Deploy this sample to your Service Catalog + +### Deploy using Azure Portal + +Clicking on the button below, will create the Managed Application definition to a Resource Group in your Azure subscription. + +[![Deploy to Azure](http://azuredeploy.net/deploybutton.png)](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2Fazure%2Fazure-managedapp-samples%2Fmaster%2FManaged%2520Application%2520Sample%2520Packages%2F201-managed-storage-account%2Fazuredeploy.json) + +### Deploy using PowerShell + +````powershell +$rgname = "" +$location = "" +$authorization = ":" +$uri = "https://raw.githubusercontent.com/Azure/azure-managedapp-samples/master/Managed Application Sample Packages/201-managed-storage-account/managedstorage.zip" + +New-AzureRmManagedApplicationDefinition -Name "ManagedStorage" ` + -ResourceGroupName $rgname ` + -DisplayName "Managed Storage Account" ` + -Description "Managed Azure Storage Account" ` + -Location $location ` + -LockLevel ReadOnly ` + -PackageFileUri $uri ` + -Authorization $authorization ` + -Verbose +```` + +### Deploy using AzureCLI + +Modify the snippet below to deploy Managed Application definition to a Resource Group in your Azure subscription + +````azureCLI +az managedapp definition create \ + --name "ManagedStorage" \ + --location \ + --resource-group \ + --lock-level ReadOnly \ + --display-name "Managed Storage Account" \ + --description "Managed Azure Storage Account" \ + --authorizations ":" \ + --package-file-uri "https://raw.githubusercontent.com/Azure/azure-managedapp-samples/master/Managed Application Sample Packages/201-managed-storage-account/managedstorage.zip" +```` + +![alt text](images/storage.png "Azure Managed Application") \ No newline at end of file diff --git a/Managed Application Sample Packages/201-managed-web-app/README.md b/Managed Application Sample Packages/201-managed-web-app/README.md new file mode 100644 index 0000000..c76a57e --- /dev/null +++ b/Managed Application Sample Packages/201-managed-web-app/README.md @@ -0,0 +1,49 @@ +# Managed Web Application (IaaS) with Azure management services + +>Note: This sample is for Managed Application in Service Catalog. For Marketplace, please see these instructions: +[**Marketplace Managed Application**](https://docs.microsoft.com/en-us/azure/managed-applications/publish-marketplace-app) + +## Deploy this sample to your Service Catalog + +### Deploy using Azure Portal + +Clicking on the button below, will create the Managed Application definition to a Resource Group in your Azure subscription. + +[![Deploy to Azure](http://azuredeploy.net/deploybutton.png)](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2Fazure%2Fazure-managedapp-samples%2Fmaster%2FManaged%2520Application%2520Sample%2520Packages%2F201-managed-web-app%2Fazuredeploy.json) + +### Deploy using PowerShell + +Modify the snippet below to deploy Managed Application definition to a Resource Group in your Azure subscription + +````powershell +$rgname = "" +$location = "" +$authorization = ":" +$uri = "https://raw.githubusercontent.com/Azure/azure-managedapp-samples/master/Managed Application Sample Packages/201-managed-web-app/managedwebapp.zip" + +New-AzureRmManagedApplicationDefinition -Name "ManagedWebApp" ` + -ResourceGroupName $rgname ` + -DisplayName "Managed Web App" ` + -Description "Managed Web App with Azure mgmt" ` + -Location $location ` + -LockLevel ReadOnly ` + -PackageFileUri $uri ` + -Authorization $authorization ` + -Verbose +```` + +### Deploy using AzureCLI + +Modify the snippet below to deploy Managed Application definition to a Resource Group in your Azure subscription + +````azureCLI +az managedapp definition create \ + --name "ManagedWebApp" \ + --location \ + --resource-group \ + --lock-level ReadOnly \ + --display-name "Managed Web Application" \ + --description "Web App with Azure mgmt" \ + --authorizations ":" \ + --package-file-uri "https://raw.githubusercontent.com/Azure/azure-managedapp-samples/master/Managed Application Sample Packages/201-managed-web-app/managedwebapp.zip" +```` \ No newline at end of file diff --git a/Managed Application Sample Packages/201-managed-web-app/azuredeploy.json b/Managed Application Sample Packages/201-managed-web-app/azuredeploy.json new file mode 100644 index 0000000..c9ce3c4 --- /dev/null +++ b/Managed Application Sample Packages/201-managed-web-app/azuredeploy.json @@ -0,0 +1,82 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "type": "string", + "defaultValue": "ManagedWebApp", + "metadata": { + "description": "Provide a name for the managed application" + } + }, + "location": { + "type": "string", + "metadata": { + "description": "Specify the Azure region to place the application definition" + } + }, + "lockLevel": { + "type": "string", + "allowedValues": [ + "ReadOnly", + "CanNotDelete" + ], + "defaultValue": "ReadOnly", + "metadata": { + "description": "Specify the resource lock being used for the managed application" + } + }, + "authorizations": { + "type": "array", + "metadata": { + "description": "Provide the authorization mapping for the managed application." + } + }, + "description": { + "type": "string", + "defaultValue": "Managed Azure IaaS Web Application", + "metadata": { + "description": "Provide a brief description of the managed application" + } + }, + "displayName": { + "type": "string", + "defaultValue": "Managed Azure Web Application", + "metadata": { + "description": "Display name for the managed application" + } + } + }, + "variables": { + "packageFileUri": "[uri(deployment().properties.templateLink.uri, 'managedwebapp.zip')]" + }, + "resources": [ + { + "apiVersion": "2017-09-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "type": "Microsoft.Solutions/applicationDefinitions", + "properties": { + "lockLevel": "[parameters('lockLevel')]", + "authorizations": "[array(parameters('authorizations'))]", + "description": "[parameters('description')]", + "displayName": "[parameters('displayName')]", + "packageFileUri": "[variables('packageFileUri')]" + } + } + ], + "outputs": { + "managedApplicationName": { + "type": "string", + "value": "[parameters('name')]" + }, + "lockLevel": { + "type": "string", + "value": "[parameters('locklevel')]" + }, + "packageFileUri": { + "type": "string", + "value": "[variables('packageFileUri')]" + } + } +} \ No newline at end of file diff --git a/Managed Application Sample Packages/201-managed-web-app/createUiDefinition.json b/Managed Application Sample Packages/201-managed-web-app/createUiDefinition.json new file mode 100644 index 0000000..d0fea6c --- /dev/null +++ b/Managed Application Sample Packages/201-managed-web-app/createUiDefinition.json @@ -0,0 +1,151 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/0.1.2-preview/CreateUIDefinition.MultiVm.json#", + "handler": "Microsoft.Azure.CreateUIDef", + "version": "0.1.2-preview", + "parameters": { + "basics": [ + {} + ], + "steps": [ + { + "name": "credentialsConfig", + "label": "VM Credential", + "subLabel": { + "preValidation": "Configure the Web App VM credentials", + "postValidation": "Done" + }, + "bladeTitle": "Credential", + "elements": [ + { + "name": "adminUsername", + "type": "Microsoft.Compute.UserNameTextBox", + "label": "User name", + "toolTip": "Admin username for the virtual machine", + "osPlatform": "Windows", + "constraints": { + "required": true + } + }, + { + "name": "adminPassword", + "type": "Microsoft.Compute.CredentialsCombo", + "label": { + "password": "Password", + "confirmPassword": "Confirm password" + }, + "toolTip": { + "password": "Admin password for the virtual machine" + }, + "osPlatform": "Windows", + "constraints": { + "required": true + } + } + ] + }, + { + "name": "vmConfig", + "label": "Web App Virtual Machine settings", + "subLabel": { + "preValidation": "Configure the virtual machine settings", + "postValidation": "Done" + }, + "bladeTitle": "Web App VM Settings", + "elements": [ + { + "name": "vmNamePrefix", + "type": "Microsoft.Common.TextBox", + "label": "Virtual Machine Name prefix", + "toolTip": "Prefix of the VM for your web app", + "defaultValue": "", + "constraints": { + "required": true, + "regex": "[a-z][a-z0-9-]{2,5}[a-z0-9]$", + "validationMessage": "Must be 3-5 characters." + } + }, + { + "name": "vmSize", + "type": "Microsoft.Compute.SizeSelector", + "label": "Virtual machine size", + "toolTip": "The size of the virtual machine for web app", + "recommendedSizes": [ + "Standard_D1_v2" + ], + "constraints": { + "allowedSizes": [ + "Standard_D1_v2" + ] + }, + "osPlatform": "Windows", + "count": 1 + } + ] + }, + { + "name": "webConfig", + "label": "Web App settings", + "subLabel": { + "preValidation": "Configure the web app endpoint", + "postValidation": "Done" + }, + "bladeTitle": "Web App Endpoint settings", + "elements": [ + { + "name": "dnsAndPublicIP", + "type": "Microsoft.Network.PublicIpAddressCombo", + "label": { + "publicIpAddress": "Public IP address", + "domainNameLabel": "DNS label" + }, + "toolTip": { + "domainNameLabel": "DNS endpoint for the Managed Web App IP address." + }, + "defaultValue": { + "publicIpAddressName": "ip01" + }, + "options": { + "hideNone": true, + "hideDomainNameLabel": false + }, + "constraints": { + "required": { + "domainNameLabel": true + } + } + }, + { + "name": "management", + "type": "Microsoft.Common.OptionsGroup", + "label": "Enable premium management?", + "defaultValue": "Yes", + "toolTip": "Select Yes to set up premium management for the virtual machines and web app", + "constraints": { + "allowedValues": [ + { + "label": "Yes", + "value": "Yes" + }, + { + "label": "No", + "value": "No" + } + ] + }, + "visible": true + } + ] + } + ], + "outputs": { + "location": "[location()]", + "vmSize": "[steps('vmConfig').vmSize]", + "vmNamePrefix": "[steps('vmConfig').vmNamePrefix]", + "userName": "[steps('credentialsConfig').adminUsername]", + "pwd": "[steps('credentialsConfig').adminPassword.password]", + "dnsName": "[steps('webConfig').dnsAndPublicIP.domainNameLabel]", + "publicIPAddressName": "[steps('webConfig').dnsAndPublicIP.name]", + "enablePremiumManagement": "[steps('webConfig').management]" + } + } +} \ No newline at end of file diff --git a/Managed Application Sample Packages/201-managed-web-app/images/webapp.png b/Managed Application Sample Packages/201-managed-web-app/images/webapp.png new file mode 100644 index 0000000..89d4fda Binary files /dev/null and b/Managed Application Sample Packages/201-managed-web-app/images/webapp.png differ diff --git a/Managed Application Sample Packages/201-managed-web-app/mainTemplate.json b/Managed Application Sample Packages/201-managed-web-app/mainTemplate.json new file mode 100644 index 0000000..cb95555 --- /dev/null +++ b/Managed Application Sample Packages/201-managed-web-app/mainTemplate.json @@ -0,0 +1,173 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Specify the location for the Azure resources" + } + }, + "vmSize": { + "type": "string", + "defaultValue": "Standard_D1_v2", + "metadata": { + "description": "Select the VM Size" + } + }, + "vmNamePrefix": { + "type": "string", + "metadata": { + "description": "Assign a prefix for the VM name" + } + }, + "userName": { + "type": "string", + "metadata": { + "description": "Specify the user name for the virtual machine guest OS" + } + }, + "pwd": { + "type": "securestring", + "metadata": { + "description": "Specify the password for the user account for the virtual machine" + } + }, + "enablePremiumManagement": { + "type": "string", + "allowedValues": [ + "Yes", + "No" + ], + "metadata": { + "description": "Select whether premium management should be enabled or not" + } + }, + "dnsName": { + "type": "string", + "metadata": { + "description": "Specify the DNS name for the managed web app" + } + }, + "publicIPAddressName": { + "type": "string", + "metadata": { + "description": "Assign a name for the public IP address" + } + } + }, + "variables": { + "artifacts": { + "logAnalytics": "[uri(deployment().properties.templateLink.uri, 'nestedtemplates/oms.json')]", + "compute": "[uri(deployment().properties.templateLink.uri, 'nestedtemplates/managedVm.json')]", + "scripts": "[uri(deployment().properties.templateLink.uri, 'scripts/ManagedWebApplication.ps1.zip')]" + }, + "logAnalyticsLocationMap": { + "eastasia": "southeastasia", + "southeastasia": "southeastasia", + "centralus": "westcentralus", + "eastus": "eastus", + "eastus2": "eastus", + "westus": "westcentralus", + "northcentralus": "westcentralus", + "southcentralus": "westcentralus", + "northeurope": "westeurope", + "westeurope": "westeurope", + "japanwest": "southeastasia", + "japaneast": "southeastasia", + "brazilsouth": "eastus", + "australiaeast": "australiasoutheast", + "australiasoutheast": "australiasoutheast", + "southindia": "southeastasia", + "centralindia": "southeastasia", + "westindia": "southeastasia", + "canadacentral": "eastus", + "canadaeast": "eastus", + "uksouth": "westeurope", + "ukwest": "westeurope", + "westcentralus": "westcentralus", + "westus2": "westcentralus", + "koreacentral": "southeastasia", + "koreasouth": "southeastasia", + "eastus2euap": "eastus" + }, + "logAnalyticsLocation": "[variables('logAnalyticsLocationMap')[parameters('location')]]", + "logAnalyticsWorkspaceName": "[concat(resourceGroup().name, '-', uniqueString('oms'))]" + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2019-10-01", + "name": "logAnalytics", + "properties": { + "mode":"Incremental", + "templateLink": { + "contentVersion": "1.0.0.0", + "uri": "[variables('artifacts').logAnalytics]" + }, + "parameters": { + "omsWorkspaceName": { + "value": "[variables('logAnalyticsWorkspaceName')]" + }, + "omsWorkspaceRegion": { + "value": "[variables('logAnalyticsLocation')]" + }, + "enablePremiumManagement": { + "value": "[parameters('enablePremiumManagement')]" + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2019-10-01", + "name": "compute", + "dependsOn": [ + "logAnalytics" + ], + "properties": { + "mode":"Incremental", + "templateLink": { + "contentVersion": "1.0.0.0", + "uri": "[variables('artifacts').compute]" + }, + "parameters": { + "location": { + "value": "[parameters('location')]" + }, + "vmSize": { + "value": "[parameters('vmSize')]" + }, + "vmNamePrefix": { + "value": "[parameters('vmNamePrefix')]" + }, + "userName": { + "value": "[parameters('userName')]" + }, + "pwd": { + "value": "[parameters('pwd')]" + }, + "dscScript": { + "value": "[variables('artifacts').scripts]" + }, + "logAnalyticsWorkspaceName": { + "value": "[variables('logAnalyticsWorkspaceName')]" + }, + "publicIPAddressName": { + "value": "[parameters('publicIPAddressName')]" + }, + "dnsName": { + "value": "[parameters('dnsName')]" + } + } + } + } + ], + "outputs": { + "applicationEndpoint": { + "type": "string", + "value": "[reference('compute').outputs.vmEndpoint.value]" + } + } +} \ No newline at end of file diff --git a/Managed Application Sample Packages/201-managed-web-app/managedwebapp.zip b/Managed Application Sample Packages/201-managed-web-app/managedwebapp.zip new file mode 100644 index 0000000..4f6ee17 Binary files /dev/null and b/Managed Application Sample Packages/201-managed-web-app/managedwebapp.zip differ diff --git a/Managed Application Sample Packages/201-managed-web-app/nestedtemplates/managedVm.json b/Managed Application Sample Packages/201-managed-web-app/nestedtemplates/managedVm.json new file mode 100644 index 0000000..aea700e --- /dev/null +++ b/Managed Application Sample Packages/201-managed-web-app/nestedtemplates/managedVm.json @@ -0,0 +1,284 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "vmNamePrefix": { + "type": "string", + "metadata": { + "description": "Assign a prefix for the VM name" + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Select the Azure region for the resources" + } + }, + "vmSize": { + "type": "string", + "defaultValue": "Standard_D1_v2", + "metadata": { + "description": "Selec the vm size" + } + }, + "userName": { + "type": "string", + "defaultValue": "azureadmin", + "metadata": { + "description": "Specify the OS username" + } + }, + "pwd": { + "type": "securestring", + "metadata": { + "description": "If Windows, specify the password for the OS username" + } + }, + "dscScript": { + "type": "string", + "metadata": { + "description": "Specify the path to the DSC artifacts" + } + }, + "logAnalyticsWorkspaceName": { + "type": "string", + "metadata": { + "description": "The name of the Azure Log Analytics workspace" + } + }, + "dnsName": { + "type": "string", + "metadata": { + "description": "Specify the DNS name for the managed web app" + } + }, + "publicIPAddressName": { + "type": "string", + "metadata": { + "description": "Assign a name for the public IP address" + } + } + }, + "variables": { + "storageAccountName": "[toLower(concat('st', uniquestring(resourceGroup().name)))]", + "vnetID": "[resourceId('Microsoft.Network/virtualnetworks', 'vmVnet')]", + "subnetRef": "[concat(variables('vnetID'),'/subnets/', 'subnet1')]", + "managementTypeWindows": { + "omsType": "MicrosoftMonitoringAgent", + "scriptType": "DSC" + }, + "osTypeWindows": { + "imageOffer": "WindowsServer", + "imageSku": "2016-Datacenter", + "imagePublisher": "MicrosoftWindowsServer" + } + }, + "resources": [ + { + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2019-06-01", + "name": "[variables('storageAccountName')]", + "location": "[parameters('location')]", + "sku": { + "name": "Standard_LRS" + }, + "kind": "Storage" + }, + { + "type": "Microsoft.Network/virtualNetworks", + "apiVersion": "2019-11-01", + "name": "vmVnet", + "location": "[parameters('location')]", + "dependsOn": [ + "[resourceId('Microsoft.Network/networkSecurityGroups/', 'NSG')]" + ], + "properties": { + "addressSpace": { + "addressPrefixes": [ + "10.0.0.0/16" + ] + }, + "subnets": [ + { + "name": "subnet1", + "properties": { + "addressPrefix": "10.0.0.0/24", + "networkSecurityGroup": { + "id": "[resourceId('Microsoft.Network/networkSecurityGroups/', 'NSG')]" + } + } + } + ] + } + }, + { + "type": "Microsoft.Network/networkSecurityGroups", + "apiVersion": "2019-11-01", + "name": "NSG", + "location": "[parameters('location')]", + "properties": { + "securityRules": [ + { + "name": "RDP", + "properties": { + "access": "Allow", + "description": "Inbound RDP rule", + "direction": "Inbound", + "destinationAddressPrefix": "*", + "protocol": "Tcp", + "destinationPortRange": 3389, + "sourcePortRange": "*", + "priority": 500, + "sourceAddressPrefix": "*" + } + }, + { + "name": "HTTP", + "properties": { + "access": "Allow", + "description": "Inbound HTTP rule", + "direction": "Inbound", + "destinationAddressPrefix": "*", + "protocol": "Tcp", + "destinationPortRange": 80, + "sourcePortRange": "*", + "priority": 550, + "sourceAddressPrefix": "*" + } + } + ] + } + }, + { + "type": "Microsoft.Network/publicIPAddresses", + "apiVersion": "2019-11-01", + "name": "[concat(parameters('publicIPAddressName'), 'IP')]", + "location": "[parameters('location')]", + "properties": { + "publicIPallocationmethod": "Dynamic", + "dnsSettings": { + "domainNameLabel": "[toLower(parameters('dnsName'))]" + } + } + }, + { + "type": "Microsoft.Network/networkInterfaces", + "apiVersion": "2019-11-01", + "name": "[concat(parameters('vmNamePrefix'), 'nic')]", + "location": "[parameters('location')]", + "dependsOn": [ + "[concat('Microsoft.Network/publicIPAddresses/', parameters('publicIPAddressName'), 'IP')]", + "[resourceId('Microsoft.Network/virtualNetworks/', 'vmVnet')]" + ], + "properties": { + "ipConfigurations": [ + { + "name": "ipconfig1", + "properties": { + "privateIPAllocationMethod": "Dynamic", + "publicIPAddress": { + "id": "[resourceId('Microsoft.Network/publicIPAddresses', concat(parameters('publicIPAddressName'), 'IP'))]" + }, + "subnet": { + "id": "[variables('subnetRef')]" + } + } + } + ] + } + }, + { + "type": "Microsoft.Compute/virtualMachines", + "apiVersion": "2019-12-01", + "name": "[concat(parameters('vmNamePrefix'), '-app')]", + "location": "[parameters('location')]", + "dependsOn": [ + "[concat('Microsoft.Storage/StorageAccounts/', variables('storageAccountName'))]", + "[concat('Microsoft.Network/networkinterfaces/', parameters('vmNamePrefix'), 'nic')]" + ], + "properties": { + "hardwareProfile": { + "vmsize": "[parameters('vmSize')]" + }, + "osProfile": { + "computername": "[concat(parameters('vmNamePrefix'), '-app')]", + "adminusername": "[parameters('username')]", + "adminpassword": "[parameters('pwd')]" + }, + "storageProfile": { + "imageReference": { + "publisher": "[variables('osTypeWindows').imagePublisher]", + "offer": "[variables('osTypeWindows').imageOffer]", + "version": "latest", + "sku": "[variables('osTypeWindows').imageSku]" + }, + "osdisk": { + "name": "osdisk", + "vhd": { + "uri": "[concat('http://', variables('storageAccountName'), '.blob.core.windows.net/', 'vhds', '/', 'osdisk','.vhd')]" + }, + "caching": "readwrite", + "createoption": "FromImage" + } + }, + "networkprofile": { + "networkinterfaces": [ + { + "id": "[resourceId('Microsoft.Network/networkinterfaces', concat(parameters('vmNamePrefix'),'nic'))]" + } + ] + } + }, + "resources": [ + { + "type": "extensions", + "apiVersion": "2017-03-30", + "name": "PowerShellDSC", + "location": "[parameters('location')]", + "dependsOn": [ + "[resourceId('Microsoft.Compute/virtualMachines/', concat(parameters('vmNamePrefix'), '-app'))]" + ], + "properties": { + "autoUpgradeMinorVersion": true, + "typeHandlerVersion": "2.20", + "publisher": "Microsoft.Powershell", + "type": "[variables('managementTypeWindows').scriptType]", + "settings": { + "configurationFunction": "ManagedWebApplication.ps1\\ManagedWebApplication", + "modulesUrl": "[parameters('dscScript')]" + } + } + }, + { + "type": "extensions", + "apiVersion": "2017-03-30", + "name": "OMS", + "location": "[parameters('location')]", + "dependsOn": [ + "[resourceId('Microsoft.Compute/virtualMachines/', concat(parameters('vmNamePrefix'), '-app'))]" + ], + "properties": { + "autoUpgradeMinorVersion": true, + "typeHandlerVersion": "1.0", + "publisher": "Microsoft.EnterpriseCloud.Monitoring", + "type": "MicrosoftMonitoringAgent", + "settings": { + "workspaceId": "[reference(resourceId('Microsoft.OperationalInsights/workspaces/', parameters('logAnalyticsWorkspaceName')), '2015-11-01-preview').customerId]", + "azureResourceId": "[resourceId('Microsoft.Compute/virtualMachines/', concat(parameters('vmNamePrefix'), '-app'))]" + }, + "protectedSettings": { + "workspaceKey": "[listKeys(resourceId('Microsoft.OperationalInsights/workspaces/', parameters('logAnalyticsWorkspaceName')), '2015-11-01-preview').primarySharedKey]" + } + } + } + ] + } + ], + "outputs": { + "vmEndpoint": { + "type": "string", + "value": "[reference(concat(parameters('publicIPAddressName'), 'IP')).dnsSettings.fqdn]" + } + } +} \ No newline at end of file diff --git a/Managed Application Sample Packages/201-managed-web-app/nestedtemplates/oms.json b/Managed Application Sample Packages/201-managed-web-app/nestedtemplates/oms.json new file mode 100644 index 0000000..87e8923 --- /dev/null +++ b/Managed Application Sample Packages/201-managed-web-app/nestedtemplates/oms.json @@ -0,0 +1,510 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "omsWorkspaceName": { + "type": "string", + "metadata": { + "description": "Assign a name for the Log Analytic Workspace Name" + } + }, + "omsWorkspaceRegion": { + "type": "string", + "metadata": { + "description": "Specify the region for your Workspace" + } + }, + "enablePremiumManagement": { + "type": "string", + "allowedValues": [ + "Yes", + "No" + ], + "metadata": { + "description": "Select whether premium management should be enabled or not" + } + } + }, + "variables": { + "batch1": { + "solutions": [ + { + "name": "[concat('Security', '(', parameters('omsWorkspaceName'), ')')]", + "marketplaceName": "Security" + }, + { + "name": "[concat('AgentHealthAssessment', '(', parameters('omsWorkspaceName'), ')')]", + "marketplaceName": "AgentHealthAssessment" + }, + { + "name": "[concat('ChangeTracking', '(', parameters('omsWorkspaceName'), ')')]", + "marketplaceName": "ChangeTracking" + }, + { + "name": "[concat('Updates', '(', parameters('omsWorkspaceName'), ')')]", + "marketplaceName": "Updates" + }, + { + "name": "[concat('AlertManagement', '(', parameters('omsWorkspaceName'), ')')]", + "marketplaceName": "AlertManagement" + }, + { + "name": "[concat('AntiMalware', '(', parameters('omsWorkspaceName'), ')')]", + "marketplaceName": "AntiMalware" + }, + { + "name": "[concat('AzureNSGAnalytics', '(', parameters('omsWorkspaceName'), ')')]", + "marketplaceName": "AzureNSGAnalytics" + } + ] + } + }, + "resources": [ + { + "apiVersion": "2015-11-01-preview", + "location": "[parameters('omsWorkspaceRegion')]", + "name": "[parameters('omsWorkspaceName')]", + "type": "Microsoft.OperationalInsights/workspaces", + "comments": "Log Analytics workspace", + "properties": { + "sku": { + "name": "pernode" + } + }, + "resources": [ + { + "name": "[concat(parameters('omsWorkspaceName'), '/', 'SoftwareUpdateFailed1')]", + "type": "Microsoft.OperationalInsights/workspaces/savedSearches", + "apiVersion": "2015-11-01-preview", + "dependsOn": [ + "[concat('Microsoft.OperationalInsights/workspaces/', parameters('omsWorkspaceName'))]" + ], + "tags": {}, + "properties": { + "ETag": "*", + "query": "Type=Event EventID=20 Source=\"Microsoft-Windows-WindowsUpdateClient\" EventLog=\"System\" TimeGenerated>NOW-24HOURS | Measure Count() By Computer", + "displayName": "A Software Update Installation Failed", + "category": "Software Updates" + } + }, + { + "name": "[concat(parameters('omsWorkspaceName'), '/', 'SoftwareUpdateFailed2')]", + "type": "Microsoft.OperationalInsights/workspaces/savedSearches", + "apiVersion": "2015-11-01-preview", + "dependsOn": [ + "[concat('Microsoft.OperationalInsights/workspaces/', parameters('omsWorkspaceName'))]" + ], + "tags": {}, + "properties": { + "ETag": "*", + "query": "Type=Event EventID=20 Source=\"Microsoft-Windows-WindowsUpdateClient\" EventLog=\"System\" TimeGenerated>NOW-168HOURS", + "displayName": "A Software Update Installation Failed", + "category": "Software Updates" + } + }, + { + "name": "[concat(parameters('omsWorkspaceName'), '/', 'Network1')]", + "type": "Microsoft.OperationalInsights/workspaces/savedSearches", + "apiVersion": "2015-11-01-preview", + "dependsOn": [ + "[concat('Microsoft.OperationalInsights/workspaces/', parameters('omsWorkspaceName'))]" + ], + "tags": {}, + "properties": { + "ETag": "*", + "query": "Type=Event EventID=4202 Source=\"TCPIP\" EventLog=\"System\" TimeGenerated>NOW-24HOURS | Measure Count() By Computer", + "displayName": "A Network adatper was disconnected from the network", + "category": "Networking" + } + }, + { + "name": "[concat(parameters('omsWorkspaceName'), '/', 'Network2')]", + "type": "Microsoft.OperationalInsights/workspaces/savedSearches", + "apiVersion": "2015-11-01-preview", + "dependsOn": [ + "[concat('Microsoft.OperationalInsights/workspaces/', parameters('omsWorkspaceName'))]" + ], + "tags": {}, + "properties": { + "ETag": "*", + "query": "Type=Event EventID=4198 OR EventID=4199 Source=\"TCPIP\" EventLog=\"System\" TimeGenerated>NOW-24HOURS", + "displayName": "Duplicate IP address has been detected", + "category": "Networking" + } + }, + { + "name": "[concat(parameters('omsWorkspaceName'), '/', 'NTFS1')]", + "type": "Microsoft.OperationalInsights/workspaces/savedSearches", + "apiVersion": "2015-11-01-preview", + "dependsOn": [ + "[concat('Microsoft.OperationalInsights/workspaces/', parameters('omsWorkspaceName'))]" + ], + "tags": {}, + "properties": { + "ETag": "*", + "query": "Type=Event EventID=98 Source=\"Microsoft-Windows-Ntfs\" EventLog=\"System\" TimeGenerated>NOW-24HOURS | Measure Count() By Computer", + "displayName": "NTFS File System Corruption", + "category": "NTFS" + } + }, + { + "name": "[concat(parameters('omsWorkspaceName'), '/', 'NTFS2')]", + "type": "Microsoft.OperationalInsights/workspaces/savedSearches", + "apiVersion": "2015-11-01-preview", + "dependsOn": [ + "[concat('Microsoft.OperationalInsights/workspaces/', parameters('omsWorkspaceName'))]" + ], + "tags": {}, + "properties": { + "ETag": "*", + "query": "Type=Event EventID=40 OR EventID=36� Source=\"DISK\" EventLog=\"System\" TimeGenerated>NOW-24HOURS | Measure Count() By Compute", + "displayName": "NTFS Quouta treshold limit reached", + "category": "NTFS" + } + }, + { + "apiVersion": "2015-11-01-preview", + "type": "datasources", + "name": "LogicalDisk1", + "dependsOn": [ + "[concat('Microsoft.OperationalInsights/workspaces/', parameters('omsWorkspaceName'))]" + ], + "kind": "WindowsPerformanceCounter", + "properties": { + "objectName": "LogicalDisk", + "instanceName": "*", + "intervalSeconds": 10, + "counterName": "Avg Disk sec/Read" + } + }, + { + "apiVersion": "2015-11-01-preview", + "type": "datasources", + "name": "LogicalDisk2", + "dependsOn": [ + "[concat('Microsoft.OperationalInsights/workspaces/', parameters('omsWorkspaceName'))]" + ], + "kind": "WindowsPerformanceCounter", + "properties": { + "objectName": "LogicalDisk", + "instanceName": "*", + "intervalSeconds": 10, + "counterName": "Avg Disk sec/Write" + } + }, + { + "apiVersion": "2015-11-01-preview", + "type": "datasources", + "name": "LogicalDisk3", + "dependsOn": [ + "[concat('Microsoft.OperationalInsights/workspaces/', parameters('omsWorkspaceName'))]" + ], + "kind": "WindowsPerformanceCounter", + "properties": { + "objectName": "LogicalDisk", + "instanceName": "*", + "intervalSeconds": 10, + "counterName": "Current Disk Queue Lenght" + } + }, + { + "apiVersion": "2015-11-01-preview", + "type": "datasources", + "name": "LogicalDisk4", + "dependsOn": [ + "[concat('Microsoft.OperationalInsights/workspaces/', parameters('omsWorkspaceName'))]" + ], + "kind": "WindowsPerformanceCounter", + "properties": { + "objectName": "LogicalDisk", + "instanceName": "*", + "intervalSeconds": 10, + "counterName": "Disk Reads/sec" + } + }, + { + "apiVersion": "2015-11-01-preview", + "type": "datasources", + "name": "LogicalDisk5", + "dependsOn": [ + "[concat('Microsoft.OperationalInsights/workspaces/', parameters('omsWorkspaceName'))]" + ], + "kind": "WindowsPerformanceCounter", + "properties": { + "objectName": "LogicalDisk", + "instanceName": "*", + "intervalSeconds": 10, + "counterName": "Disk Transfers/sec" + } + }, + { + "apiVersion": "2015-11-01-preview", + "type": "datasources", + "name": "LogicalDisk6", + "dependsOn": [ + "[concat('Microsoft.OperationalInsights/workspaces/', parameters('omsWorkspaceName'))]" + ], + "kind": "WindowsPerformanceCounter", + "properties": { + "objectName": "LogicalDisk", + "instanceName": "*", + "intervalSeconds": 10, + "counterName": "Disk Writes/sec" + } + }, + { + "apiVersion": "2015-11-01-preview", + "type": "datasources", + "name": "LogicalDisk7", + "dependsOn": [ + "[concat('Microsoft.OperationalInsights/workspaces/', parameters('omsWorkspaceName'))]" + ], + "kind": "WindowsPerformanceCounter", + "properties": { + "objectName": "LogicalDisk", + "instanceName": "*", + "intervalSeconds": 10, + "counterName": "Free Megabytes" + } + }, + { + "apiVersion": "2015-11-01-preview", + "type": "datasources", + "name": "LogicalDisk8", + "dependsOn": [ + "[concat('Microsoft.OperationalInsights/workspaces/', parameters('omsWorkspaceName'))]" + ], + "kind": "WindowsPerformanceCounter", + "properties": { + "objectName": "LogicalDisk", + "instanceName": "*", + "intervalSeconds": 10, + "counterName": "% Free Space" + } + }, + { + "apiVersion": "2015-11-01-preview", + "type": "datasources", + "name": "Memory1", + "dependsOn": [ + "[concat('Microsoft.OperationalInsights/workspaces/', parameters('omsWorkspaceName'))]" + ], + "kind": "WindowsPerformanceCounter", + "properties": { + "objectName": "Memory", + "instanceName": "*", + "intervalSeconds": 10, + "counterName": "Available MBytes" + } + }, + { + "apiVersion": "2015-11-01-preview", + "type": "datasources", + "name": "Memory2", + "dependsOn": [ + "[concat('Microsoft.OperationalInsights/workspaces/', parameters('omsWorkspaceName'))]" + ], + "kind": "WindowsPerformanceCounter", + "properties": { + "objectName": "Memory", + "instanceName": "*", + "intervalSeconds": 10, + "counterName": "% Committed Bytes In Use" + } + }, + { + "apiVersion": "2015-11-01-preview", + "type": "datasources", + "name": "Network1", + "dependsOn": [ + "[concat('Microsoft.OperationalInsights/workspaces/', parameters('omsWorkspaceName'))]" + ], + "kind": "WindowsPerformanceCounter", + "properties": { + "objectName": "Network Adapter", + "instanceName": "*", + "intervalSeconds": 10, + "counterName": "Bytes Received/sec" + } + }, + { + "apiVersion": "2015-11-01-preview", + "type": "datasources", + "name": "Network2", + "dependsOn": [ + "[concat('Microsoft.OperationalInsights/workspaces/', parameters('omsWorkspaceName'))]" + ], + "kind": "WindowsPerformanceCounter", + "properties": { + "objectName": "Network Adapter", + "instanceName": "*", + "intervalSeconds": 10, + "counterName": "Bytes Sent/sec" + } + }, + { + "apiVersion": "2015-11-01-preview", + "type": "datasources", + "name": "Network3", + "dependsOn": [ + "[concat('Microsoft.OperationalInsights/workspaces/', parameters('omsWorkspaceName'))]" + ], + "kind": "WindowsPerformanceCounter", + "properties": { + "objectName": "Network Adapter", + "instanceName": "*", + "intervalSeconds": 10, + "counterName": "Bytes Total/sec" + } + }, + { + "apiVersion": "2015-11-01-preview", + "type": "datasources", + "name": "CPU1", + "dependsOn": [ + "[concat('Microsoft.OperationalInsights/workspaces/', parameters('omsWorkspaceName'))]" + ], + "kind": "WindowsPerformanceCounter", + "properties": { + "objectName": "Processor", + "instanceName": "_Total", + "intervalSeconds": 10, + "counterName": "% Processor Time" + } + }, + { + "apiVersion": "2015-11-01-preview", + "type": "datasources", + "name": "CPU2", + "dependsOn": [ + "[concat('Microsoft.OperationalInsights/workspaces/', parameters('omsWorkspaceName'))]" + ], + "kind": "WindowsPerformanceCounter", + "properties": { + "objectName": "System", + "instanceName": "*", + "intervalSeconds": 10, + "counterName": "Processor Queue Lenght" + } + }, + { + "apiVersion": "2015-11-01-preview", + "type": "datasources", + "name": "System", + "dependsOn": [ + "[concat('Microsoft.OperationalInsights/workspaces/', parameters('omsWorkspaceName'))]" + ], + "kind": "WindowsEvent", + "properties": { + "eventLogName": "System", + "eventTypes": [ + { + "eventType": "Error" + }, + { + "eventType": "Warning" + } + ] + } + }, + { + "apiVersion": "2015-11-01-preview", + "type": "datasources", + "name": "Application", + "dependsOn": [ + "[concat('Microsoft.OperationalInsights/workspaces/', parameters('omsWorkspaceName'))]" + ], + "kind": "WindowsEvent", + "properties": { + "eventLogName": "Application", + "eventTypes": [ + { + "eventType": "Error" + }, + { + "eventType": "Warning" + } + ] + } + }, + { + "apiVersion": "2015-11-01-preview", + "type": "datasources", + "name": "IISLog", + "dependsOn": [ + "[concat('Microsoft.OperationalInsights/workspaces/', parameters('omsWorkspaceName'))]" + ], + "kind": "IISLogs", + "properties": { + "state": "OnPremiseEnabled" + } + }, + { + "apiVersion": "2015-11-01-preview", + "type": "datasources", + "name": "Syslog", + "dependsOn": [ + "[concat('Microsoft.OperationalInsights/workspaces/', parameters('omsWorkspaceName'))]" + ], + "kind": "LinuxSyslog", + "properties": { + "syslogName": "kern", + "syslogSeverities": [ + { + "severity": "emerg" + }, + { + "severity": "alert" + }, + { + "severity": "crit" + }, + { + "severity": "err" + }, + { + "severity": "warning" + } + ] + } + }, + { + "apiVersion": "2015-11-01-preview", + "type": "datasources", + "name": "SyslogCollection", + "dependsOn": [ + "[concat('Microsoft.OperationalInsights/workspaces/', parameters('omsWorkspaceName'))]" + ], + "kind": "LinuxSyslogCollection", + "properties": { + "state": "Enabled" + } + } + ] + }, + { + "condition": "[equals(parameters('enablePremiumManagement'), 'Yes')]", + "apiVersion": "2015-11-01-preview", + "type": "Microsoft.OperationsManagement/solutions", + "name": "[concat(variables('batch1').solutions[copyIndex()].Name)]", + "location": "[parameters('omsWorkspaceRegion')]", + "dependsOn": [ + "[concat('Microsoft.OperationalInsights/workspaces/', parameters('omsWorkspaceName'))]" + ], + "copy": { + "name": "solutionCopy", + "count": "[length(variables('batch1').solutions)]" + }, + "properties": { + "workspaceResourceId": "[resourceId('Microsoft.OperationalInsights/workspaces', parameters('omsWorkspaceName'))]" + }, + "plan": { + "name": "[variables('batch1').solutions[copyIndex()].name]", + "product": "[concat('OMSGallery/', variables('batch1').solutions[copyIndex()].marketplaceName)]", + "promotionCode": "", + "publisher": "Microsoft" + } + } + ], + "outputs": {} +} \ No newline at end of file diff --git a/Managed Application Sample Packages/201-managed-web-app/scripts/ManagedWebApplication.ps1.zip b/Managed Application Sample Packages/201-managed-web-app/scripts/ManagedWebApplication.ps1.zip new file mode 100644 index 0000000..453e8d7 Binary files /dev/null and b/Managed Application Sample Packages/201-managed-web-app/scripts/ManagedWebApplication.ps1.zip differ diff --git a/Managed Application Sample Packages/readme.md b/Managed Application Sample Packages/readme.md new file mode 100644 index 0000000..42a05a0 --- /dev/null +++ b/Managed Application Sample Packages/readme.md @@ -0,0 +1 @@ +This folder contains the different Managed Application samples for your reference. diff --git a/Managed Application Sample Packages/refresh-zipfiles.sh b/Managed Application Sample Packages/refresh-zipfiles.sh new file mode 100755 index 0000000..8ad9710 --- /dev/null +++ b/Managed Application Sample Packages/refresh-zipfiles.sh @@ -0,0 +1,35 @@ +#!/bin/bash +script_name=`basename "$0"` +echo "Running $script_name" +# Setup error handling +tempfiles=( ) +cleanup() { + rm -f "${tempfiles[@]}" +} +trap cleanup 0 + +error() { + local parent_lineno="$1" + local message="$2" + local code="${3:-1}" + if [[ -n "$message" ]] ; then + echo "Error on or near line ${parent_lineno}: ${message}; exiting with status ${code}" + else + echo "Error on or near line ${parent_lineno}; exiting with status ${code}" + fi + + exit "${code}" +} +trap 'error ${LINENO}' ERR + +refresh_file() { + zipfile=$1 + dirname=$(dirname $zipfile) + filename=$(basename $zipfile) + pushd $dirname + zip -f $filename + popd +} + +export -f refresh_file +find . -name "*.zip" -exec bash -c "refresh_file \"{}\"" \; \ No newline at end of file diff --git a/Powerhsell Samples/Retireve-Service-Catalog-ARM-Templates/readme.md b/Powerhsell Samples/Retireve-Service-Catalog-ARM-Templates/readme.md new file mode 100644 index 0000000..622c0bb --- /dev/null +++ b/Powerhsell Samples/Retireve-Service-Catalog-ARM-Templates/readme.md @@ -0,0 +1 @@ +This powershell sample returns a list of all ServiceCatalogDefinitions and URIs to the ARM templates they contain. diff --git a/Powerhsell Samples/Retireve-Service-Catalog-ARM-Templates/retrieve-service-catalog-ARM-templates.ps1 b/Powerhsell Samples/Retireve-Service-Catalog-ARM-Templates/retrieve-service-catalog-ARM-templates.ps1 new file mode 100644 index 0000000..0428d2b --- /dev/null +++ b/Powerhsell Samples/Retireve-Service-Catalog-ARM-Templates/retrieve-service-catalog-ARM-templates.ps1 @@ -0,0 +1,11 @@ +$resourceDefinitions = Get-AzResource | Where-Object {$_.ResourceType -eq "Microsoft.Solutions/applicationDefinitions"} + +foreach ($resourceDefinition in $resourceDefinitions) +{ + $applicationDefinition = Get-AzManagedApplicationDefinition -ResourceGroupName $resourceDefinition.ResourceGroupName + + #Get properties + $applicationTemplate = $applicationDefinition.Properties.Artifacts[0].Uri + $mainTemplate = $applicationTemplate -replace "applicationResourceTemplate.json", "mainTemplate.json" + echo $resourceDefinition.Name; $mainTemplate + } diff --git a/Powerhsell Samples/readme.md b/Powerhsell Samples/readme.md new file mode 100644 index 0000000..8cfdbbf --- /dev/null +++ b/Powerhsell Samples/readme.md @@ -0,0 +1,23 @@ +# Azure Managed Application Powershell samples + +## Abstract +This repository contains samples of Azure Managed Application Powershell samples that can be used as reference for creating and consuming managed applications. + +## Documentation + +[Azure Managed Application overview](https://docs.microsoft.com/en-us/azure/azure-resource-manager/managed-application-overview) + +[Create and publish Azure Managed Applications to Service Catalog](https://docs.microsoft.com/en-us/azure/azure-resource-manager/managed-application-publishing) + +[Create and publish Azure Managed Applications to Azure Marketplace](https://docs.microsoft.com/en-us/azure/azure-resource-manager/managed-application-author-marketplace) + +[Consume an Azure Managed Application](https://docs.microsoft.com/en-us/azure/azure-resource-manager/managed-application-consumption) + +[Create UI definitions](https://docs.microsoft.com/en-us/azure/azure-resource-manager/managed-application-createuidefinition-overview) + + +## Contributing + +To contribute and get started, please visit our [**contribution guide**](./1-contribution-guide/README.md#contribution-guide). + +*This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.* diff --git a/README.md b/README.md index a6fd71f..b09d153 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,23 @@ +# Azure Managed Application samples ## Abstract This repository contains samples of Azure Managed Applications that can be used as reference for creating and consuming managed applications. +## Documentation + +[Azure Managed Application overview](https://docs.microsoft.com/en-us/azure/azure-resource-manager/managed-application-overview) + +[Create and publish Azure Managed Applications to Service Catalog](https://docs.microsoft.com/en-us/azure/azure-resource-manager/managed-application-publishing) + +[Create and publish Azure Managed Applications to Azure Marketplace](https://docs.microsoft.com/en-us/azure/azure-resource-manager/managed-application-author-marketplace) + +[Consume an Azure Managed Application](https://docs.microsoft.com/en-us/azure/azure-resource-manager/managed-application-consumption) + +[Create UI definitions](https://docs.microsoft.com/en-us/azure/azure-resource-manager/managed-application-createuidefinition-overview) + + ## Contributing -This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. +To contribute and get started, please visit our [**contribution guide**](./1-contribution-guide/README.md#contribution-guide). + +*This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.* diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..e138ec5 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,41 @@ + + +## Security + +Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). + +If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below. + +## Reporting Security Issues + +**Please do not report security vulnerabilities through public GitHub issues.** + +Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report). + +If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey). + +You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc). + +Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: + + * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) + * Full paths of source file(s) related to the manifestation of the issue + * The location of the affected source code (tag/branch/commit or direct URL) + * Any special configuration required to reproduce the issue + * Step-by-step instructions to reproduce the issue + * Proof-of-concept or exploit code (if possible) + * Impact of the issue, including how an attacker might exploit the issue + +This information will help us triage your report more quickly. + +If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs. + +## Preferred Languages + +We prefer all communications to be in English. + +## Policy + +Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd). + + diff --git a/desktop.ini b/desktop.ini new file mode 100644 index 0000000..eec42e1 --- /dev/null +++ b/desktop.ini @@ -0,0 +1,2 @@ +[LocalizedFileNames] +GetApplicationDefinition.sh=@GetApplicationDefinition.sh,0 diff --git a/notification-endpoint/README.md b/notification-endpoint/README.md new file mode 100644 index 0000000..a27f13a --- /dev/null +++ b/notification-endpoint/README.md @@ -0,0 +1,5 @@ +# Notification Endpoint Samples + +If you have seen [notification endpoints](https://docs.microsoft.com/azure/azure-resource-manager/managed-applications/publish-notifications) and would like to use these, this sample shows how to process those messages in [C#](./csharp), [Python](./python), and [TypeScript](./typescript). + +All three samples are implemented as [Azure Functions](https://docs.microsoft.com/azure/azure-functions/functions-overview). As you read through the samples, it may not be obvious what value they provide. The hard part in getting all of this to work is getting the URL exactly correct. When setting the endpoint, the actual call to the notification endpoint always appends the "/resource" to the endpoint path. So, if the endpoint is `https://mynotification.azurewebsites.net/api`, the call will go to `https://mynotification.azurewebsites.net/api/resource` when the notification endpoint is called. Everything else is simple to deal with. \ No newline at end of file diff --git a/notification-endpoint/csharp/EventType.cs b/notification-endpoint/csharp/EventType.cs new file mode 100644 index 0000000..b9b70f0 --- /dev/null +++ b/notification-endpoint/csharp/EventType.cs @@ -0,0 +1,9 @@ +namespace Microsoft.IndustryExperiences +{ + public enum EventType{ + PUT, + PATCH, + DELETE, + } +} + diff --git a/notification-endpoint/csharp/ManagedAppSubscriberInfo.cs b/notification-endpoint/csharp/ManagedAppSubscriberInfo.cs new file mode 100644 index 0000000..9c2506f --- /dev/null +++ b/notification-endpoint/csharp/ManagedAppSubscriberInfo.cs @@ -0,0 +1,62 @@ +using System; +using System.Net; +using Newtonsoft.Json; + +namespace Microsoft.IndustryExperiences +{ + public class ManagedAppSubscriberInfo + { + public static ManagedAppSubscriberInfo FromNotification(Notification notification) + { + var retval = new ManagedAppSubscriberInfo() + { + Plan = notification.Plan.Name, + Product = notification.Plan.Product, + Version = notification.Plan.Version, + ApplicationId = notification.ApplicationId, + InstallTime = notification.EventTime, + LastUpdateTime = DateTime.MinValue, + }; + + return retval; + } + + [JsonProperty] + public string Plan { get; set; } + + [JsonProperty] + public string Product { get; set; } + + [JsonProperty] + public string Version { get; set; } + + [JsonIgnore] + public string ApplicationId + { + get;set; + } + + [JsonProperty(PropertyName = "id")] + public string Id { get + { + return WebUtility.UrlEncode(ApplicationId); + } + set + { + ApplicationId = WebUtility.UrlDecode(value); + } + } + + [JsonProperty] + public DateTime InstallTime { get; set; } + + [JsonProperty] + public DateTime LastUpdateTime { get; set; } + + public override string ToString() + { + return JsonConvert.SerializeObject(this); + } + } +} + diff --git a/notification-endpoint/csharp/ManagedAppWebHook.cs b/notification-endpoint/csharp/ManagedAppWebHook.cs new file mode 100644 index 0000000..a10a2d4 --- /dev/null +++ b/notification-endpoint/csharp/ManagedAppWebHook.cs @@ -0,0 +1,107 @@ +using System; +using System.IO; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Azure.WebJobs; +using Microsoft.Azure.WebJobs.Extensions.Http; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json; +using Microsoft.Azure.Cosmos; + +namespace Microsoft.IndustryExperiences +{ + + public static class ManagedAppWebHook + { + // The Azure Cosmos DB endpoint for running this sample. + private static readonly string EndpointUri = Environment.GetEnvironmentVariable("CosmosEndpointUri"); + // The primary key for the Azure Cosmos account. + private static readonly string PrimaryKey = Environment.GetEnvironmentVariable("CosmosKey"); + + private static readonly string DatabaseId = "ManagedApps"; + private static readonly string ContainerId = "Subscribers"; + + [FunctionName("resource")] + public static async Task Run( + [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = null)] HttpRequest req, + ILogger log) + { + log.LogInformation("C# HTTP trigger function processed a request."); + + ActionResult result = new OkResult(); + + try + { + var requestBody = await new StreamReader(req.Body).ReadToEndAsync(); + log.LogInformation(requestBody); + var data = JsonConvert.DeserializeObject(requestBody); + + log.LogInformation(requestBody); + switch(data.ProvisioningState){ + case ProvisioningState.Accepted: + break; + case ProvisioningState.Failed: + break; + case ProvisioningState.Deleted: + await RemoveSubscriber(data, log); + break; + case ProvisioningState.Succeeded: + await SaveSubscriberAsync(data, log); + break; + } + } + catch(Exception ex) + { + log.LogError(-1, ex, "Exception occurred during processing"); + result = new StatusCodeResult(500); + } + + return result; + } + + private static async Task GetContainerAsync() + { + var client = new CosmosClient(EndpointUri, PrimaryKey); + Database database = await client.CreateDatabaseIfNotExistsAsync(DatabaseId); + Container container = await database.CreateContainerIfNotExistsAsync( + ContainerId, $"/{nameof(ManagedAppSubscriberInfo.Product)}"); + + return container; + } + + private static async Task RemoveSubscriber(Notification data, ILogger log) + { + try + { + var container = await GetContainerAsync(); + var record = ManagedAppSubscriberInfo.FromNotification(data); + await container.DeleteItemAsync(record.Id, new PartitionKey(record.Product)); + + return true; + } + catch (Exception ex) + { + log.LogError(ex, "Issue saving subscription info"); + return false; + } + } + + private static async Task SaveSubscriberAsync(Notification data, ILogger log) + { + try + { + var container = await GetContainerAsync(); + var record = ManagedAppSubscriberInfo.FromNotification(data); + await container.UpsertItemAsync(record, new PartitionKey(record.Product)); + return record; + } + catch (Exception ex) + { + log.LogError(ex, "Issue saving subscription info"); + return null; + } + } + } +} + diff --git a/notification-endpoint/csharp/Notification.cs b/notification-endpoint/csharp/Notification.cs new file mode 100644 index 0000000..bfb6db2 --- /dev/null +++ b/notification-endpoint/csharp/Notification.cs @@ -0,0 +1,30 @@ +using System; +using Newtonsoft.Json; + +namespace Microsoft.IndustryExperiences +{ + public class Notification + { + [JsonProperty("eventType")] + public EventType EventType{get;set;} + + [JsonProperty("applicationId")] + public string ApplicationId{get;set;} + + [JsonProperty("eventTime")] + public DateTime EventTime{get;set;} + + [JsonProperty("provisioningState")] + public ProvisioningState ProvisioningState{get;set;} + + [JsonProperty("applicationDefinitionId")] + public string ApplicationDefinitionId{get;set;} + + [JsonProperty("error")] + public NotificationError Error{get;set;} + + [JsonProperty("plan")] + public PlanInfo Plan{get;set;} + } +} + diff --git a/notification-endpoint/csharp/NotificationError.cs b/notification-endpoint/csharp/NotificationError.cs new file mode 100644 index 0000000..749c39a --- /dev/null +++ b/notification-endpoint/csharp/NotificationError.cs @@ -0,0 +1,17 @@ +using Newtonsoft.Json; + +namespace Microsoft.IndustryExperiences +{ + public class NotificationError + { + [JsonProperty("code")] + public string Code{get;set;} + + [JsonProperty("message")] + public string Message{get;set;} + + [JsonProperty("details")] + public NotificationErrorDetails[] Details{get;set;} + } +} + diff --git a/notification-endpoint/csharp/NotificationErrorDetails.cs b/notification-endpoint/csharp/NotificationErrorDetails.cs new file mode 100644 index 0000000..3dad281 --- /dev/null +++ b/notification-endpoint/csharp/NotificationErrorDetails.cs @@ -0,0 +1,14 @@ +using Newtonsoft.Json; + +namespace Microsoft.IndustryExperiences +{ + public class NotificationErrorDetails + { + [JsonProperty("code")] + public string Code{get;set;} + + [JsonProperty("message")] + public string Message{get;set;} + } +} + diff --git a/notification-endpoint/csharp/PlanInfo.cs b/notification-endpoint/csharp/PlanInfo.cs new file mode 100644 index 0000000..ab07df3 --- /dev/null +++ b/notification-endpoint/csharp/PlanInfo.cs @@ -0,0 +1,20 @@ +using Newtonsoft.Json; + +namespace Microsoft.IndustryExperiences +{ + public class PlanInfo + { + [JsonProperty("name")] + public string Name{get;set;} + + [JsonProperty("product")] + public string Product{get;set;} + + [JsonProperty("publisher")] + public string Publisher{get;set;} + + [JsonProperty("version")] + public string Version {get;set;} + } +} + diff --git a/notification-endpoint/csharp/ProvisioningState.cs b/notification-endpoint/csharp/ProvisioningState.cs new file mode 100644 index 0000000..0cf1bd9 --- /dev/null +++ b/notification-endpoint/csharp/ProvisioningState.cs @@ -0,0 +1,11 @@ +namespace Microsoft.IndustryExperiences +{ + public enum ProvisioningState{ + Accepted, + Succeeded, + Failed, + Deleting, + Deleted, + } +} + diff --git a/notification-endpoint/csharp/functions.csproj b/notification-endpoint/csharp/functions.csproj new file mode 100644 index 0000000..95432d6 --- /dev/null +++ b/notification-endpoint/csharp/functions.csproj @@ -0,0 +1,19 @@ + + + netcoreapp2.1 + v2 + + + + + + + + PreserveNewest + + + PreserveNewest + Never + + + \ No newline at end of file diff --git a/notification-endpoint/csharp/host.json b/notification-endpoint/csharp/host.json new file mode 100644 index 0000000..f08505a --- /dev/null +++ b/notification-endpoint/csharp/host.json @@ -0,0 +1,3 @@ +{ + "version": "2.0" +} \ No newline at end of file diff --git a/notification-endpoint/csharp/local.settings.json b/notification-endpoint/csharp/local.settings.json new file mode 100644 index 0000000..98f115f --- /dev/null +++ b/notification-endpoint/csharp/local.settings.json @@ -0,0 +1,9 @@ +{ + "IsEncrypted": false, + "Values": { + "AzureWebJobsStorage": "", + "FUNCTIONS_WORKER_RUNTIME": "dotnet", + "CosmosEndpointUri": "", + "CosmosKey": "" + } +} \ No newline at end of file diff --git a/notification-endpoint/csharp/vals.txt b/notification-endpoint/csharp/vals.txt new file mode 100644 index 0000000..429ccea --- /dev/null +++ b/notification-endpoint/csharp/vals.txt @@ -0,0 +1,3 @@ +f=3[2d4c4BSyOq?IZYth[uF=y5G5oaPO +tenant 72f988bf-86f1-41af-91ab-2d7cd011db47 +clientid bef3730f-fca4-475c-87fb-3311534798a6 \ No newline at end of file diff --git a/notification-endpoint/deploy-with-notification-endpoint/README.md b/notification-endpoint/deploy-with-notification-endpoint/README.md new file mode 100644 index 0000000..862e6c3 --- /dev/null +++ b/notification-endpoint/deploy-with-notification-endpoint/README.md @@ -0,0 +1,12 @@ +# Deploy with notification endpoint + +If you have implemented [notifications](https://docs.microsoft.com/azure/azure-resource-manager/managed-applications/publish-notifications) for your managed application and want to deploy your application to the [Azure Service Catalog](https://docs.microsoft.com/azure/azure-resource-manager/managed-applications/publish-service-catalog-app) from the command line, you need to push an ARM template. This sample does exactly that, including populating the ARM template with your values. + +The sample script is written to be deployed from the command line or from a CI/CD environment. In a CI/CD environment, the contents of variables.conf should be in the environment and configured via the tooling. The values should not be checked into source control. + +## Run the script +1. Update [variables.conf](./variables.conf) to values that are appropriate for your Azure environment. +1. Run deploy.sh from a bash shell. This will work on Linux, macOS, and Windows via [WSL](https://docs.microsoft.com/windows/wsl/about). + +## Use the item +You can then test the installation by deploying the managed application via the [portal](https://portal.azure.com/#blade/Microsoft_Azure_Appliance/ManagedAppsHubBlade/publishServiceCatalogAppDefinition). \ No newline at end of file diff --git a/notification-endpoint/deploy-with-notification-endpoint/createUiDefinition.json b/notification-endpoint/deploy-with-notification-endpoint/createUiDefinition.json new file mode 100644 index 0000000..48612ce --- /dev/null +++ b/notification-endpoint/deploy-with-notification-endpoint/createUiDefinition.json @@ -0,0 +1,47 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/0.1.2-preview/CreateUIDefinition.MultiVm.json#", + "handler": "Microsoft.Azure.CreateUIDef", + "version": "0.1.2-preview", + "parameters": { + "basics": [ + { + "name": "username", + "type": "Microsoft.Common.TextBox", + "label": "User name", + "defaultValue": "", + "toolTip": "Use only allowed characters", + "constraints": { + "required": true, + "regex": "^[a-z0-9A-Z]{6,30}$", + "validationMessage": "Only alphanumeric characters are allowed, and the value must be 6-30 characters long." + }, + "visible": true + }, + { + "name": "password", + "type": "Microsoft.Compute.CredentialsCombo", + "label": { + "password": "Password", + "confirmPassword": "Confirm password" + }, + "toolTip": { + "password": "" + }, + "constraints": { + "required": true + }, + "options": { + "hideConfirmation": false + }, + "osPlatform": "Windows", + "visible": true + } + ], + "steps": [ + ], + "outputs": { + "username" : "[basics('username')]", + "password": "[basics('password').password]" + } + } + } \ No newline at end of file diff --git a/notification-endpoint/deploy-with-notification-endpoint/deploy.sh b/notification-endpoint/deploy-with-notification-endpoint/deploy.sh new file mode 100755 index 0000000..e51fc43 --- /dev/null +++ b/notification-endpoint/deploy-with-notification-endpoint/deploy.sh @@ -0,0 +1,114 @@ +#!/bin/bash +echo "Running deploy.sh" +# Setup error handling +tempfiles=( ) + +. variables.conf +zip_file=simpleApp.zip + +azlogin() { + az login --service-principal -u $service_principal_client_id -p $service_principal_client_secret --tenant $azure_ad_tenant_id + az account set -s $azure_subscription_id +} + +azlogout() { + az logout +} + +cleanup() { + rm -f "${tempfiles[@]}" +} +trap cleanup 0 + +error() { + local parent_lineno="$1" + local message="$2" + local code="${3:-1}" + if [[ -n "$message" ]] ; then + echo "Error on or near line ${parent_lineno}: ${message}; exiting with status ${code}" + else + echo "Error on or near line ${parent_lineno}; exiting with status ${code}" + fi + azlogout + + exit "${code}" +} +trap 'error ${LINENO}' ERR + + +zip_files() { + echo "Creating zip file" + + rm $zip_file + zip $zip_file createUiDefinition.json mainTemplate.json +} + +initialize_environment() { + echo "Checking for resource group $managed_app_resource_group" + resource_group_exists=$(az group exists --name $managed_app_resource_group) + if [[ $resource_group_exists == "false" ]]; then + echo "Creating resource group $managed_app_resource_group" + az group create --name $managed_app_resource_group --location $resource_group_location + fi + + echo "Checking for storage account $storage_account" + storage_account_exists=$(az storage account check-name --name $storage_account --query nameAvailable --output tsv) + if [[ $storage_account_exists == "false" ]]; then + echo "Creating storage account $storage_account" + az storage account create --name $storage_account \ + --location $resource_group_location \ + --kind StorageV2 \ + --resource-group $managed_app_resource_group \ + --sku Standard_LRS + fi + + echo "Checking for storage container $storage_container" + storage_connection_string=$(az storage account show-connection-string --name $storage_account --query connectionString --output tsv) + container_exists=$(az storage container exists --name $storage_container --connection-string $storage_connection_string) + + if [[ $container_exists == "false" ]]; then + echo "Creating storage container $storage_container" + az storage container create --name $storage_container --connection-string $storage_connection_string + fi +} + +upload_file() { + storage_connection_string=$(az storage account show-connection-string --name $storage_account --query connectionString --output tsv) + blob_exists=$(az storage blob exists --container-name $storage_container --connection-string $storage_connection_string --name $zip_file --output tsv) + #echo "Blob exists: $blob_exists" + if [[ blob_exists == "True" ]]; then + az storage blob delete --container-name $storage_container --connection-string $storage_connection_string --name $zip_file + fi + + az storage blob upload -f $zip_file --container-name $storage_container --connection-string $storage_connection_string -n $zip_file +} + +create_managed_app() { + if [[ $(az managedapp definition list --resource-group $managed_app_resource_group --output tsv | grep $managed_app_name | wc -c) != 0 ]]; then + echo "Deleting managed app definition $managed_app_name" + az managedapp definition delete --resource-group $managed_app_resource_group --name $managed_app_name + fi + + package_location="https://$storage_account.blob.core.windows.net/$storage_container/$zip_file" + + parameters_file=$(cat ./managed-app-definition/parameters.json) + parameters_file=$(echo "${parameters_file/__APPNAME__/$managed_app_name}") + parameters_file=$(echo "${parameters_file/__LOCATION__/$resource_group_location}") + parameters_file=$(echo "${parameters_file/__DESCRIPTION__/$managed_app_description}") + parameters_file=$(echo "${parameters_file/__DISPLAY_NAME__/$managed_app_display_name}") + parameters_file=$(echo "${parameters_file/__PACKAGE_FILE_URI__/$package_location}") + parameters_file=$(echo "${parameters_file/__PRINCIPAL_ID__/$managed_app_principal_id}") + parameters_file=$(echo "${parameters_file/__ROLE_DEFINITION_ID__/$managed_app_role_definition_id}") + parameters_file=$(echo "${parameters_file/__NOTIFICATION_ENDPOINT__/$managed_app_notification_endpoint}") + echo $parameters_file > ./parameters.json + echo "Deploying the managed app definition $managed_app_name" + az deployment group create -g $managed_app_resource_group --template-file ./managed-app-definition/template.json --parameters @parameters.json +} + +zip_files +azlogin +initialize_environment +upload_file +create_managed_app + +azlogout diff --git a/notification-endpoint/deploy-with-notification-endpoint/mainTemplate.json b/notification-endpoint/deploy-with-notification-endpoint/mainTemplate.json new file mode 100644 index 0000000..aea937a --- /dev/null +++ b/notification-endpoint/deploy-with-notification-endpoint/mainTemplate.json @@ -0,0 +1,30 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "username": { + "type": "string" + }, + "password": { + "type": "string" + } + }, + "resources": [ + { + "apiVersion": "2019-10-01", + "name": "pid-", + "type": "Microsoft.Resources/deployments", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [] + } + } + } + ], + "outputs": { + + } +} \ No newline at end of file diff --git a/notification-endpoint/deploy-with-notification-endpoint/managed-app-definition/parameters.json b/notification-endpoint/deploy-with-notification-endpoint/managed-app-definition/parameters.json new file mode 100644 index 0000000..b204982 --- /dev/null +++ b/notification-endpoint/deploy-with-notification-endpoint/managed-app-definition/parameters.json @@ -0,0 +1,56 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "__APPNAME__" + }, + "location": { + "value": "__LOCATION__" + }, + "managementPolicy": { + "value": { + "mode": "Managed" + } + }, + "lockLevel": { + "value": "readonly" + }, + "description": { + "value": "__DESCRIPTION__" + }, + "displayName": { + "value": "__DISPLAY_NAME__" + }, + "packageFileUri": { + "value": "__PACKAGE_FILE_URI__" + }, + "authorizations": { + "value": [ + { + "principalId": "__PRINCIPAL_ID__", + "roleDefinitionId": "__ROLE_DEFINITION_ID__" + } + ] + }, + "lockingPolicy": { + "value": { + "allowedActions": [] + } + }, + "notificationPolicy": { + "value": { + "notificationEndpoints": [ + { + "uri": "__NOTIFICATION_ENDPOINT__" + } + ] + } + }, + "deploymentPolicy": { + "value": { + "deploymentMode": "Incremental" + } + } + } +} \ No newline at end of file diff --git a/notification-endpoint/deploy-with-notification-endpoint/managed-app-definition/template.json b/notification-endpoint/deploy-with-notification-endpoint/managed-app-definition/template.json new file mode 100644 index 0000000..62b68e0 --- /dev/null +++ b/notification-endpoint/deploy-with-notification-endpoint/managed-app-definition/template.json @@ -0,0 +1,58 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string" + }, + "managementPolicy": { + "type": "object" + }, + "lockLevel": { + "type": "string" + }, + "authorizations": { + "type": "array" + }, + "description": { + "type": "string" + }, + "displayName": { + "type": "string" + }, + "packageFileUri": { + "type": "string" + }, + "lockingPolicy": { + "type": "object" + }, + "notificationPolicy": { + "type": "object" + }, + "deploymentPolicy": { + "type": "object" + } + }, + "resources": [ + { + "apiVersion": "2019-07-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "type": "Microsoft.Solutions/applicationDefinitions", + "properties": { + "lockLevel": "[parameters('lockLevel')]", + "authorizations": "[parameters('authorizations')]", + "description": "[parameters('description')]", + "displayName": "[parameters('displayName')]", + "packageFileUri": "[parameters('packageFileUri')]", + "managementPolicy": "[parameters('managementPolicy')]", + "lockingPolicy": "[parameters('lockingPolicy')]", + "notificationPolicy": "[parameters('notificationPolicy')]", + "deploymentPolicy": "[parameters('deploymentPolicy')]" + } + } + ] +} \ No newline at end of file diff --git a/notification-endpoint/deploy-with-notification-endpoint/variables.conf b/notification-endpoint/deploy-with-notification-endpoint/variables.conf new file mode 100644 index 0000000..6f05321 --- /dev/null +++ b/notification-endpoint/deploy-with-notification-endpoint/variables.conf @@ -0,0 +1,23 @@ +service_principal_client_id=http:// +service_principal_client_secret= +azure_ad_tenant_id= +azure_subscription_id= +managed_app_resource_group= +resource_group_location= +storage_account= +storage_container= +managed_app_display_name="" +managed_app_name="" +managed_app_notification_endpoint="" +managed_app_description="" +# Group object ID +managed_app_principal_id="" + +# Role for the object, from https://docs.microsoft.com/azure/role-based-access-control/built-in-roles +# For reference: +# Contributor: b24988ac-6180-42a0-ab88-20f7382dd24c +# Owner: 8e3af657-a8ff-443c-a75c-2fe8c4bcb635 +# The other roles cannot be used since this is for control of the +# Managed Resource Group. +managed_app_role_definition_id="8e3af657-a8ff-443c-a75c-2fe8c4bcb635" + diff --git a/notification-endpoint/python/.funcignore b/notification-endpoint/python/.funcignore new file mode 100644 index 0000000..0678ea2 --- /dev/null +++ b/notification-endpoint/python/.funcignore @@ -0,0 +1,5 @@ +.git* +.vscode +local.settings.json +test +.venv \ No newline at end of file diff --git a/notification-endpoint/python/.gitignore b/notification-endpoint/python/.gitignore new file mode 100644 index 0000000..a10127b --- /dev/null +++ b/notification-endpoint/python/.gitignore @@ -0,0 +1,130 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don’t work, or not +# install all needed dependencies. +#Pipfile.lock + +# celery beat schedule file +celerybeat-schedule + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# Azure Functions artifacts +bin +obj +appsettings.json +local.settings.json +.python_packages \ No newline at end of file diff --git a/notification-endpoint/python/host.json b/notification-endpoint/python/host.json new file mode 100644 index 0000000..d2059a4 --- /dev/null +++ b/notification-endpoint/python/host.json @@ -0,0 +1,3 @@ +{ + "version": "2.0" +} diff --git a/notification-endpoint/python/proxies.json b/notification-endpoint/python/proxies.json new file mode 100644 index 0000000..b385252 --- /dev/null +++ b/notification-endpoint/python/proxies.json @@ -0,0 +1,4 @@ +{ + "$schema": "http://json.schemastore.org/proxies", + "proxies": {} +} diff --git a/notification-endpoint/python/requirements.txt b/notification-endpoint/python/requirements.txt new file mode 100644 index 0000000..f86a15a --- /dev/null +++ b/notification-endpoint/python/requirements.txt @@ -0,0 +1 @@ +azure-functions \ No newline at end of file diff --git a/notification-endpoint/python/resource/__init__.py b/notification-endpoint/python/resource/__init__.py new file mode 100644 index 0000000..2b6c73a --- /dev/null +++ b/notification-endpoint/python/resource/__init__.py @@ -0,0 +1,20 @@ +import logging +import json +import azure.functions as func + + +def main(req: func.HttpRequest) -> func.HttpResponse: + logging.info('Python HTTP trigger function processed a request.') + + try: + req_body = req.get_json() + request_as_text = json.dumps(req_body, default=lambda o: o.__dict__) + logging.info(request_as_text) + except e as Exception: + logging.exception(e) + return func.HttpResponse( + "An error occurred.", + status_code=500 + ) + + return func.HttpResponse(f"Success") diff --git a/notification-endpoint/python/resource/function.json b/notification-endpoint/python/resource/function.json new file mode 100644 index 0000000..1c8afb7 --- /dev/null +++ b/notification-endpoint/python/resource/function.json @@ -0,0 +1,19 @@ +{ + "scriptFile": "__init__.py", + "bindings": [ + { + "authLevel": "anonymous", + "type": "httpTrigger", + "direction": "in", + "name": "req", + "methods": [ + "post" + ] + }, + { + "type": "http", + "direction": "out", + "name": "$return" + } + ] +} diff --git a/notification-endpoint/python/resource/sample.dat b/notification-endpoint/python/resource/sample.dat new file mode 100644 index 0000000..2e60943 --- /dev/null +++ b/notification-endpoint/python/resource/sample.dat @@ -0,0 +1,3 @@ +{ + "name": "Azure" +} \ No newline at end of file diff --git a/notification-endpoint/typescript/.funcignore b/notification-endpoint/typescript/.funcignore new file mode 100644 index 0000000..5179222 --- /dev/null +++ b/notification-endpoint/typescript/.funcignore @@ -0,0 +1,7 @@ +*.js.map +*.ts +.git* +.vscode +local.settings.json +test +tsconfig.json \ No newline at end of file diff --git a/notification-endpoint/typescript/.gitignore b/notification-endpoint/typescript/.gitignore new file mode 100644 index 0000000..772851c --- /dev/null +++ b/notification-endpoint/typescript/.gitignore @@ -0,0 +1,94 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# TypeScript v1 declaration files +typings/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env +.env.test + +# parcel-bundler cache (https://parceljs.org/) +.cache + +# next.js build output +.next + +# nuxt.js build output +.nuxt + +# vuepress build output +.vuepress/dist + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TypeScript output +dist +out + +# Azure Functions artifacts +bin +obj +appsettings.json +local.settings.json \ No newline at end of file diff --git a/notification-endpoint/typescript/host.json b/notification-endpoint/typescript/host.json new file mode 100644 index 0000000..d2059a4 --- /dev/null +++ b/notification-endpoint/typescript/host.json @@ -0,0 +1,3 @@ +{ + "version": "2.0" +} diff --git a/notification-endpoint/typescript/package-lock.json b/notification-endpoint/typescript/package-lock.json new file mode 100644 index 0000000..97a1d04 --- /dev/null +++ b/notification-endpoint/typescript/package-lock.json @@ -0,0 +1,20 @@ +{ + "name": "node", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@azure/functions": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@azure/functions/-/functions-1.0.3.tgz", + "integrity": "sha512-/D+sz6LgWT+A6RRW2zhwlwhKqqDSxL6HCF1Q1lN0iXolD2FfNFZpzrOxGyGYEEXp/5Dtjp12bcRTBhMH1cBi2Q==", + "dev": true + }, + "typescript": { + "version": "3.7.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.7.3.tgz", + "integrity": "sha512-Mcr/Qk7hXqFBXMN7p7Lusj1ktCBydylfQM/FZCk5glCNQJrCUKPkMHdo9R0MTFWsC/4kPFvDS0fDPvukfCkFsw==", + "dev": true + } + } +} diff --git a/notification-endpoint/typescript/package.json b/notification-endpoint/typescript/package.json new file mode 100644 index 0000000..69c7280 --- /dev/null +++ b/notification-endpoint/typescript/package.json @@ -0,0 +1,17 @@ +{ + "name": "node", + "version": "1.0.0", + "description": "", + "scripts": { + "build": "tsc", + "watch": "tsc -w", + "prestart": "npm run build", + "start": "func start", + "test": "echo \"No tests yet...\"" + }, + "dependencies": {}, + "devDependencies": { + "@azure/functions": "^1.0.2-beta2", + "typescript": "^3.3.3" + } +} diff --git a/notification-endpoint/typescript/proxies.json b/notification-endpoint/typescript/proxies.json new file mode 100644 index 0000000..b385252 --- /dev/null +++ b/notification-endpoint/typescript/proxies.json @@ -0,0 +1,4 @@ +{ + "$schema": "http://json.schemastore.org/proxies", + "proxies": {} +} diff --git a/notification-endpoint/typescript/resource/function.json b/notification-endpoint/typescript/resource/function.json new file mode 100644 index 0000000..c7f9bea --- /dev/null +++ b/notification-endpoint/typescript/resource/function.json @@ -0,0 +1,19 @@ +{ + "bindings": [ + { + "authLevel": "anonymous", + "type": "httpTrigger", + "direction": "in", + "name": "req", + "methods": [ + "post" + ] + }, + { + "type": "http", + "direction": "out", + "name": "res" + } + ], + "scriptFile": "../dist/resource/index.js" +} diff --git a/notification-endpoint/typescript/resource/index.ts b/notification-endpoint/typescript/resource/index.ts new file mode 100644 index 0000000..19d4993 --- /dev/null +++ b/notification-endpoint/typescript/resource/index.ts @@ -0,0 +1,35 @@ +import { AzureFunction, Context, HttpRequest } from "@azure/functions" + +const httpTrigger: AzureFunction = async function (context: Context, req: HttpRequest): Promise { + context.log('HTTP trigger function processed a request.'); + var data = req.body; + + try { + if (data) { + context.log(req.body); + context.res = { + // status: 200, /* Defaults to 200 */ + body: "Processed" + }; + } + else { + context.res = { + status: 400, + body: "Please POST data in the message body." + }; + } + } catch(e) { + if (e instanceof Error) { + context.log.error(e.message); + } + else { + context.log.error("An unknown error occurred of type " + e.constructor.name); + } + context.res = { + status: 500, + body: "An error occurred and has been logged." + }; + } +}; + +export default httpTrigger; diff --git a/notification-endpoint/typescript/resource/sample.dat b/notification-endpoint/typescript/resource/sample.dat new file mode 100644 index 0000000..2e60943 --- /dev/null +++ b/notification-endpoint/typescript/resource/sample.dat @@ -0,0 +1,3 @@ +{ + "name": "Azure" +} \ No newline at end of file diff --git a/notification-endpoint/typescript/tsconfig.json b/notification-endpoint/typescript/tsconfig.json new file mode 100644 index 0000000..77d91aa --- /dev/null +++ b/notification-endpoint/typescript/tsconfig.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "es6", + "outDir": "dist", + "rootDir": ".", + "sourceMap": true, + "strict": false + } +} diff --git a/samples/readme.md b/samples/readme.md deleted file mode 100644 index 044fd1c..0000000 --- a/samples/readme.md +++ /dev/null @@ -1 +0,0 @@ -This folder contains the different managed application samples for your reference.