diff --git a/samples/101-managed-publishing-script/Publish-ManagedApp.ps1 b/samples/101-managed-publishing-script/Publish-ManagedApp.ps1 new file mode 100644 index 0000000..c99a301 --- /dev/null +++ b/samples/101-managed-publishing-script/Publish-ManagedApp.ps1 @@ -0,0 +1,211 @@ +[cmdletbinding()] +param( + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string] $SubscriptionId, + + [Parameter()] + [ValidateNotNullOrEmpty()] + [string] $Location = 'westcentralus', + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string] $ResourceGroupName, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string] $Name, + + [Parameter()] + [string] $DisplayName, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string] $Description, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string] $PrincipalId, + + [Parameter()] + [ValidateNotNullOrEmpty()] + [string] $RoleDefinitionId = '8e3af657-a8ff-443c-a75c-2fe8c4bcb635', + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string] $StorageAccountName, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string] $StorageContainerName, + + [Parameter()] + [ValidateSet('None','ReadOnly')] + [string] $LockLevel = 'ReadOnly' +) + +Function Write-Verbose +{ + Param( + [Parameter(Position=1)] + [string]$Message + ) + + Microsoft.PowerShell.Utility\Write-Verbose ("{0} - {1}" -f (Get-Date).ToString("HH:mm:ss"),$Message) +} + +if($DisplayName -eq "") +{ + $DisplayName = $Name +} + +$Error.Clear() + +$here = Split-Path -Parent $MyInvocation.MyCommand.Path +if($here -eq $null) {$here="."} + +$TemplateParameters = @{ + "managedAppName" = $Name; + "lockLevel" = $LockLevel; + "authorizations" = @( + @{ + "principalId" = $PrincipalId; + "roleDefinitionId" = $RoleDefinitionId + } + ); + "description" = $Description; + "displayName" = $DisplayName; + "packageFileUri" = "https://$StorageAccountName.blob.core.windows.net/$StorageContainerName/$Name.zip" +} + +ForEach($managedAppFile In @("createUiDefinition.json","mainTemplate.json")) +{ + Write-Verbose "Verifying if $managedAppFile exists" + if(Test-Path "$here\$managedAppFile") + { + Write-Verbose "$managedAppFile exists. Checking proper cases in the file name" + if((Get-ChildItem "$here\$managedAppFile" | Select-Object -ExpandProperty Name) -cne $managedAppFile) + { + Write-Error "$managedAppFile exists but its name doesn't match the expected cases. Please verify and try again" + Exit(-1) + } + } + else + { + Write-Error "$managedAppFile doesn't exists" + Exit(-1) + } +} + +Write-Verbose "Verifying output folder" +if(Test-Path "$here\output") +{ + Write-Verbose "Cleaning Up output folder" + Get-ChildItem "$here\output" | Remove-Item -force | Out-Null +} +else +{ + Write-Verbose "Creating output folder" + New-Item -Path "$here\output" -ItemType Directory | Out-Null +} + +Write-Verbose "Verify if managedAppTemplate.json exists" +if(!(Test-Path "$here\managedAppTemplate.json")) +{ + Write-Error "$managedAppTemplate not found in the current script location" + Exit(-1) +} + +Get-ChildItem "$here\*" -Include mainTemplate.json, createUiDefinition.json | Compress-Archive -DestinationPath "$here\output\$Name.zip" + +Write-Verbose "Logging in..." +$context = $null +try { + $context = Get-AzureRmContext +} +catch { + +} + +if($context.Subscription.Id -eq $null) +{ + Login-AzureRmAccount | Out-Null + + if($context.Subscription.Id -eq $null) + { + Write-Warning "Action cancelled" + Exit(-1) + } +} + +Write-Verbose "Logged into Azure" + +Write-Verbose "Selecting subscription $subscriptionId" +Select-AzureRmSubscription -SubscriptionId $subscriptionId | Out-Null + +Write-Verbose "Verifying if managedApp provider is registered" +If(!(Get-AzureRmResourceProvider -ProviderNamespace "Microsoft.Solutions" -ErrorAction SilentlyContinue)) +{ + Write-Verbose "Registering Resource Provider" + Register-AzureRmResourceProvider -ProviderNamespace "Microsoft.Solutions" | Out-Null +} + +Write-Verbose "Verifying if $ResourceGroupName exists in $Location" +If(!(Get-AzureRmResourceGroup -Name $ResourceGroupName -ErrorAction SilentlyContinue)) +{ + Write-Verbose "$ResourceGroupName not found, proceeding to create it" + New-AzureRmResourceGroup -Name $ResourceGroupName -Location $Location -Force | Out-Null +} +else { + Write-Verbose "Using existing resource group $ResourceGroupName at $Location" +} + +Write-Verbose "Verifying if Storage Account ($StorageAccountName) exists" +If(!(Get-AzureRmStorageAccount -Name $StorageAccountName -ResourceGroupName $ResourceGroupName -ErrorAction SilentlyContinue)) +{ + Write-Verbose "$StorageAccountName not found, proceeding to create it" + try { + New-AzureRmStorageAccount -ResourceGroupName $ResourceGroupName -Name $StorageAccountName -Location $Location -SkuName 'Standard_LRS' -ErrorAction Stop | Out-Null + } + catch { + Write-Error "Error while attempting to create $StorageAccountName storage account" + Write-Error $error[0].Exception + Exit(-2) + } +} +else { + Write-Verbose "Using existing storage account $StorageAccountName at $Location" +} + +Write-Verbose "Obtain the Storage Account authentication keys using Azure Resource Manager (ARM)" +$Keys = Get-AzureRmStorageAccountKey -ResourceGroupName $ResourceGroupName -Name $StorageAccountName; + +Write-Verbose "Use the Azure.Storage module to create a Storage Authentication Context" +$StorageContext = New-AzureStorageContext -StorageAccountName $StorageAccountName -StorageAccountKey $Keys[0].Value; + +If(!(Get-AzureStorageContainer -Context $StorageContext -Name $StorageContainerName -ErrorAction SilentlyContinue)) +{ + Write-Verbose "Creating blob container ($StorageContainerName) in the Storage Account" + New-AzureStorageContainer -Context $StorageContext -Name $StorageContainerName -Permission Blob | Out-Null +} +else { + Write-Verbose "Using existing blob container ($StorageContainerName)" +} + +Write-Verbose "Verifying if Managed Application exists in $managedAppResourceGroupName" +$managedApp = Get-AzureRmResource | Where-Object {$_.ResourceType -like "Microsoft.Solutions/applicationDefinitions" -and $_.Name -eq $Name} +if($managedApp) +{ + Write-Warning "$Name exists and it will be removed" + Remove-AzureRmResource -Force -Verbose -ResourceId $managedApp.ResourceId | Out-Null +} +else +{ + Write-Verbose "$Name not found." +} + +Write-Verbose "Uploading ManagedApp Content" +Set-AzureStorageBlobContent -File "$here\output\$Name.zip" -Container $StorageContainerName -Context $StorageContext -Force | Out-Null + +Write-Verbose "Publishing Managed App..." +New-AzureRmResourceGroupDeployment -ResourceGroupName $ResourceGroupName -TemplateFile "$here\managedAppTemplate.json" -TemplateParameterObject $TemplateParameters \ No newline at end of file diff --git a/samples/101-managed-publishing-script/README.md b/samples/101-managed-publishing-script/README.md new file mode 100644 index 0000000..3a379c3 --- /dev/null +++ b/samples/101-managed-publishing-script/README.md @@ -0,0 +1,27 @@ +# Powershell Script to deploy a ManagedApp + +When working with ManagedApps, you might find yourself publishing over and over the definition till you get it as you want it; in order to simplify the process, this script helps you to deploy the definition and creates a storage account/blob (if it doesn't exists) according to the requirements. + +## The Files + +1. Public-ManagedApp.ps1 + +*Note: CreateUiDefinition.json and mainTemplate.json must exists in the same location as the ps1.* + +Powershell script to deploy the managed app. It will create, if it doesn't exist, a Storage Account and a container (with Blob security) in order to deploy the ManagedApp definition. Parameters: + +* SubscriptionId: GUID with the subscription you want to deploy the definition. +* Location: Azure DC where you want to publish the managed app. This is where the resource group will be created. (Default: westcentralus) +* ResourceGroupName: Self-explanatory +* Name: ManagedApp Name +* DisplayName: ManagedApp Display Name +* Description: ManagedApp Description +* PrincipalId: Azure AD GUID of the group/user you want to grant access to the managed resource group +* RoleDefinitionId: Azure RBAC role to be granted to PrincipalId in the managed resource group (Default: "8e3af657-a8ff-443c-a75c-2fe8c4bcb635" => Owner) +* StorageAccountName: Self-explanatory +* StorageContainerName: Self-explanatory +* LockLevel: User access level to the managed resource group. (Default: ReadOnly) + +2.  managedAppTemplate.json + +This file is required in the same location as the PS1 since it will be used to do the actual deployment to Azure \ No newline at end of file diff --git a/samples/101-managed-publishing-script/managedAppTemplate.json b/samples/101-managed-publishing-script/managedAppTemplate.json new file mode 100644 index 0000000..aaa900a --- /dev/null +++ b/samples/101-managed-publishing-script/managedAppTemplate.json @@ -0,0 +1,39 @@ +{ + "$schema": "http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "managedAppName": { + "type": "string" + }, + "lockLevel": { + "type": "string" + }, + "authorizations": { + "type": "array" + }, + "description": { + "type": "string" + }, + "displayName": { + "type": "string" + }, + "packageFileUri": { + "type": "string" + } + }, + "resources": [ + { + "apiVersion": "2017-09-01", + "name": "[parameters('managedAppName')]", + "location": "[ResourceGroup().location]", + "type": "Microsoft.Solutions/applicationDefinitions", + "properties": { + "lockLevel": "[parameters('lockLevel')]", + "authorizations": "[parameters('authorizations')]", + "description": "[parameters('description')]", + "displayName": "[parameters('displayName')]", + "packageFileUri": "[parameters('packageFileUri')]" + } + } + ] +} \ No newline at end of file