Skip to content

Instantly share code, notes, and snippets.

@brucedkyle
Last active May 7, 2020 18:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save brucedkyle/7e4a837e5e144cfc92e9c84381caa5d1 to your computer and use it in GitHub Desktop.
Save brucedkyle/7e4a837e5e144cfc92e9c84381caa5d1 to your computer and use it in GitHub Desktop.
Boilerplate to deploy ARM Templates
#Requires -Version 5.1
#Requires -Modules Az.Resources, Az.Storage
<#
.SYNOPSIS
Deploys the resource using the boilerplate template
.DESCRIPTION
Deploys the boilerplate template to the resource group.
.PARAMETER SubscriptionID
The Azure Subscription ID, such as "9f241d6e-16e2-4b2b-a485-cc546f04799b". Uses the current subscription as the default.
.PARAMETER ResourceGroupName
Mandatory. Resource group name. The resource group needs to exist, otherwise the script throws an error.
.PARAMETER ProjectName
Mandatory.
Should follow naming convention https://docs.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/naming-and-tagging
Note: the boilerplate template provides the default prefix to the name, such as "vnet-$resourceName
Note: the boilerplate template fixes the default prefix and appends the name to make it unique for storage accounts
Suggested: $location_abbr + "-" + "$project_name" + "$environment"
.PARAMETER Mode
Default to Incremental.
Complete: In complete mode, Resource Manager deletes resources that exist in the resource group but are not specified in the template.
Incremental: In incremental mode, Resource Manager leaves unchanged resources that exist in the resource group but are not specified in the template.
.PARAMETER Tags
The tags. If absent, the template will use the default tags.
$createdData = Get-Date -Format "yyyy-MM-dd"
$tags = @{"Cost Center"=$costCenter; "Location"=$location; "Environment"= $environment; "Project"=$OrganizationName; "Owner"=$owner; "Created Date"=$createdData; "Tier"="Management"; "Application name"=$appName }
.NOTES
Version: 1.0.2
Author: Bruce Kyle
Creation Date: 5/7/2020
Purpose/Change: Publish as GitHub Gist
Requires:
- resource group must exist
- boilerplate.deploy.json, which deploys a storage account
- The storage account as deployed does not pass Security Center requirements
Copyright 2020 Stretegic Datatech LLC
License: MIT https://opensource.org/licenses/MIT
.EXAMPLE
$resourceGroupName = "rg-testme"
$tags = @{ "Cost Center" = "DevTest"; "Location"="West US 2" }
New-AzResourceGroup -Name $resourceGroupName -Location "West US 2" -Tags $tags
.\boilerplate-armtemplate.ps1 -SubscriptionID dfa5bf78-5552-4332-ba0b-f9efdaa816e5 `
-ResourceGroupName $resourceGroupName `
-ProjectName "test-ing" `
-Mode "Complete" `
-Tags $tags -Verbose
#>
[CmdletBinding(SupportsShouldProcess=$True)]
#--------[Params]---------------
Param(
[Parameter(Mandatory=$false)] [string] $SubscriptionID,
[Parameter(Mandatory)] [string] $ResourceGroupName,
[Parameter(Mandatory)] [string] $ProjectName,
[Parameter(Mandatory=$false)] [string] $Mode = "Incremental",
[Parameter(Mandatory=$false)] [object] $Tags = $null
)
#--------[Script]---------------
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
pushd $PSScriptRoot
try {
####
# Set subscription
####
if ($SubscriptionId -eq $null) {
$SubscriptionId = (Get-AzContext).Subscription.SubscriptionId
}
Set-AzContext -Name "AzureContext" -SubscriptionId $SubscriptionId -Force
####
# Basic error checking for the resource group and resource group name
####
If ( $ResourceGroupName.Length -gt 90 ) {
$error = "Resource group name '$ResourceGroupName' is too long. Must be fewer than 90 characters."
Write-Output $error
throw $error
}
Get-AzResourceGroup -Name $ResourceGroupName -ErrorVariable notPresent -ErrorAction SilentlyContinue
if ($notPresent)
{
# ResourceGroup doesn't exist
$error = "Resource group '$ResourceGroupName' does not exist. Create resource group before calling this Cmdlet"
Write-Output $error
throw $error
}
#must be shorter than 64 characters after the resource names are appended with up to 5 characters
If ( $ProjectName.Length -gt 59 ) {
$error = "Resource name '$ProjectName' is too long. Must be fewer than 59 characters."
Write-Output $error
throw $error
}
####
# Prepare to add resource
####
$ProjectName = $ProjectName.ToLower()
# need to remove the hyphens from the resource name for storage accounts
$paramObject = @{
'resourceName' = $ProjectName -replace '-'
}
If ($PSBoundParameters.ContainsKey('Tags')) {
$paramObject += @{'resourceTags' = $Tags}
}
$deploymentName = $ProjectName + "-resource-deployment"
$parameters = @{
'Name' = $deploymentName
'ResourceGroupName' = $ResourceGroupName
'TemplateFile' = '.\boilerplate.deploy.json'
'TemplateParameterObject' = $paramObject
'Mode' = $Mode
}
Write-Verbose "Deploying resource: $ProjectName"
New-AzResourceGroupDeployment @parameters
<#
New-AzResourceGroupDeployment -ResourceGroupName "ContosoEngineering" `
-TemplateFile "D:\Azure\Templates\EngineeringSite.json" `
-TemplateParameterFile "D:\Azure\Templates\EngSiteParms.json"
#>
#case matters on return value keys
$resourceID = (Get-AzResourceGroupDeployment `
-ResourceGroupName $ResourceGroupName `
-Name $deploymentName).Outputs.resourceId.value
Write-Verbose "Resource deployment successful: $resourceID"
}
catch
{
Write-Host "Deployment failed: $ProjectName" -ForegroundColor Red
$ErrorMessage = $_.Exception.Message
Write-Host $ErrorMessage
}
finally
{
popd
}
#!/bin/bash
:' Boilerplate bash script
.NOTES
Version: 1.0.1
Author: Bruce Kyle
Creation Date: 5/7/2020
Purpose/Change: Not yet complete
Requires:
- resource group must exist
- boilerplate.deploy.json, which deploys a storage account
Copyright 2020 Stretegic Datatech LLC
License: MIT https://opensource.org/licenses/MIT
.EXAMPLE
chmod a+x boilerplate-deploy.sh
bash boilerplate-deploy.sh --subscription
'
function displayHelp () {
# Using a here doc with standard out.
cat <<-END
Usage:
------
-h | --help
Display this help
-v | --verbose
Provide additional debugging information.
--subscriptionID
The subscription ID. Defaults to the current subscription id
-n | --resourceGroupName
The name of the resource group
--projectName
The name of the project. It used when creating the names of the resources
--mode
Complete or Incremental. Default is Incremental
-t | --tags
The tags to apply to the resources
Example:
chmod a+x boilerplate-deploy.sh
TAGS='"Cost Center" = "DevTest"; "Location"="West US 2"'
az group create --name $resourceGroupName --location "West US 2" -tags $TAGS
bash boilerplate-deploy.sh --subscriptionID dfa5bf78-5552-4332-ba0b-f9efdaa816e5 \
--resourceGroupName $resourceGroupName \
--projectName "test-ing" \
--mode "Complete" \
--tags $TAGS --verbose
Requires jq
END
}
## defaults
VERBOSE=false
MODE="Incremental"
TAGS=""
SUBSCRIPTIONID=az account list | jq -r '.[].id'
## get from args
for arg in "$@"
do
if [ "$arg" == "--help" ] || [ "$arg" == "-h" ]
then
displayHelp
exit
;;
fi
if [ "$arg" == "--verbose" ] || [ "$arg" == "-v" ]
then
VERBOSE=true
fi
if [ "$arg" == "--subscriptionID" ] || [ "$arg" == "-s" ]
then
SUBSCRIPTIONID=$arg
fi
if [ "$arg" == "--resourceGroupName" ] || [ "$arg" == "-n" ]
then
RESOURCEGROUPNAME=$arg
fi
if [ "$arg" == "--projectName" ] || [ "$arg" == "-p" ]
then
PROJECTNAME=$arg
fi
if [ "$arg" == "--tags" ] || [ "$arg" == "-t" ]
then
TAGS=$arg
fi
done
if VERBOSE
then
echo "subscriptionID: "$SUBSCRIPTIONID
echo "resourceGroupName: "$RESOURCEGROUPNAME
echo "projectName: "$PROJECTNAME
echo "tags: "$TAGS
echo "mode: "$MODE
echo "verbose: "$VERBOSE
fi
# _main()
#
# Usage:
# _main [<options>] [<arguments>]
{ # try
command1 && \
TEMPLATEFILE=".\boilerplate-deploy.json" && \
DEPLOYMENT=$(az deployment group create \
--name deploy-$PROJECTNAME \
--resource-group $RESOURCEGROUPNAME \
--template-file $TEMPLATEFILE)
echo "deploy-$PROJECTNAME complete"
if VERBOSE
then
echo "reployment results: "$DEPLOYMENT
fi
} || { # catch
# save log for exception
}
# Call `_main` after everything has been defined.
_main "$@"
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"resourceName": {
"type": "string",
"metadata": {
"description": "Specifies the name of the resourcee."
}
},
"resourceTags": {
"type": "object",
"defaultValue": {
"Cost Center": "Admin"
}
}
},
"variables": {
"uniqueString": "[uniqueString(subscription().id, parameters('resourceName'))]",
"storageAccountName": "[toLower(substring(replace(concat('st', parameters('resourceName'), variables('uniqueString'), variables('uniqueString')), '-', ''), 0, 23) )]"
},
"resources": [
{
"apiVersion": "2019-06-01",
"kind": "StorageV2",
"location": "[resourceGroup().location]",
"name": "[variables('storageAccountName')]",
"properties": {
"supportsHttpsTrafficOnly": true
},
"sku": {
"name": "Standard_LRS"
},
"type": "Microsoft.Storage/storageAccounts",
"tags": "[parameters('resourceTags')]"
}
],
"outputs": {
"resourceId": {
"type": "string",
"value": "[resourceId('Microsoft.Storage/storageAccounts',variables('storageAccountName'))]"
},
"storageAccountName": {
"type": "string",
"value": "[variables('storageAccountName')]"
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment