├── Deploy-AzureResourceGroup.ps1
├── Deployment.targets
├── README.md
├── azuredeploy.json
├── azuredeploy.parameters.json
└── resource-group-template.deployproj
/Deploy-AzureResourceGroup.ps1:
--------------------------------------------------------------------------------
1 | #Requires -Version 3.0
2 | #Requires -Module AzureRM.Resources
3 | #Requires -Module Azure.Storage
4 |
5 | Param(
6 | [string] [Parameter(Mandatory=$true)] $ResourceGroupLocation,
7 | [string] $ResourceGroupName = 'csv-to-email',
8 | [switch] $UploadArtifacts,
9 | [string] $StorageAccountName,
10 | [string] $StorageContainerName = $ResourceGroupName.ToLowerInvariant() + '-stageartifacts',
11 | [string] $TemplateFile = 'LogicApp.json',
12 | [string] $TemplateParametersFile = 'LogicApp.parameters.json',
13 | [string] $ArtifactStagingDirectory = '.',
14 | [string] $DSCSourceFolder = 'DSC',
15 | [switch] $ValidateOnly
16 | )
17 |
18 | try {
19 | [Microsoft.Azure.Common.Authentication.AzureSession]::ClientFactory.AddUserAgent("VSAzureTools-$UI$($host.name)".replace(' ','_'), '2.9.6')
20 | } catch { }
21 |
22 | $ErrorActionPreference = 'Stop'
23 | Set-StrictMode -Version 3
24 |
25 | function Format-ValidationOutput {
26 | param ($ValidationOutput, [int] $Depth = 0)
27 | Set-StrictMode -Off
28 | return @($ValidationOutput | Where-Object { $_ -ne $null } | ForEach-Object { @(' ' * $Depth + ': ' + $_.Message) + @(Format-ValidationOutput @($_.Details) ($Depth + 1)) })
29 | }
30 |
31 | $OptionalParameters = New-Object -TypeName Hashtable
32 | $TemplateFile = [System.IO.Path]::GetFullPath([System.IO.Path]::Combine($PSScriptRoot, $TemplateFile))
33 | $TemplateParametersFile = [System.IO.Path]::GetFullPath([System.IO.Path]::Combine($PSScriptRoot, $TemplateParametersFile))
34 |
35 | if ($UploadArtifacts) {
36 | # Convert relative paths to absolute paths if needed
37 | $ArtifactStagingDirectory = [System.IO.Path]::GetFullPath([System.IO.Path]::Combine($PSScriptRoot, $ArtifactStagingDirectory))
38 | $DSCSourceFolder = [System.IO.Path]::GetFullPath([System.IO.Path]::Combine($PSScriptRoot, $DSCSourceFolder))
39 |
40 | # Parse the parameter file and update the values of artifacts location and artifacts location SAS token if they are present
41 | $JsonParameters = Get-Content $TemplateParametersFile -Raw | ConvertFrom-Json
42 | if (($JsonParameters | Get-Member -Type NoteProperty 'parameters') -ne $null) {
43 | $JsonParameters = $JsonParameters.parameters
44 | }
45 | $ArtifactsLocationName = '_artifactsLocation'
46 | $ArtifactsLocationSasTokenName = '_artifactsLocationSasToken'
47 | $OptionalParameters[$ArtifactsLocationName] = $JsonParameters | Select -Expand $ArtifactsLocationName -ErrorAction Ignore | Select -Expand 'value' -ErrorAction Ignore
48 | $OptionalParameters[$ArtifactsLocationSasTokenName] = $JsonParameters | Select -Expand $ArtifactsLocationSasTokenName -ErrorAction Ignore | Select -Expand 'value' -ErrorAction Ignore
49 |
50 | # Create DSC configuration archive
51 | if (Test-Path $DSCSourceFolder) {
52 | $DSCSourceFilePaths = @(Get-ChildItem $DSCSourceFolder -File -Filter '*.ps1' | ForEach-Object -Process {$_.FullName})
53 | foreach ($DSCSourceFilePath in $DSCSourceFilePaths) {
54 | $DSCArchiveFilePath = $DSCSourceFilePath.Substring(0, $DSCSourceFilePath.Length - 4) + '.zip'
55 | Publish-AzureRmVMDscConfiguration $DSCSourceFilePath -OutputArchivePath $DSCArchiveFilePath -Force -Verbose
56 | }
57 | }
58 |
59 | # Create a storage account name if none was provided
60 | if ($StorageAccountName -eq '') {
61 | $StorageAccountName = 'stage' + ((Get-AzureRmContext).Subscription.SubscriptionId).Replace('-', '').substring(0, 19)
62 | }
63 |
64 | $StorageAccount = (Get-AzureRmStorageAccount | Where-Object{$_.StorageAccountName -eq $StorageAccountName})
65 |
66 | # Create the storage account if it doesn't already exist
67 | if ($StorageAccount -eq $null) {
68 | $StorageResourceGroupName = 'ARM_Deploy_Staging'
69 | New-AzureRmResourceGroup -Location "$ResourceGroupLocation" -Name $StorageResourceGroupName -Force
70 | $StorageAccount = New-AzureRmStorageAccount -StorageAccountName $StorageAccountName -Type 'Standard_LRS' -ResourceGroupName $StorageResourceGroupName -Location "$ResourceGroupLocation"
71 | }
72 |
73 | # Generate the value for artifacts location if it is not provided in the parameter file
74 | if ($OptionalParameters[$ArtifactsLocationName] -eq $null) {
75 | $OptionalParameters[$ArtifactsLocationName] = $StorageAccount.Context.BlobEndPoint + $StorageContainerName
76 | }
77 |
78 | # Copy files from the local storage staging location to the storage account container
79 | New-AzureStorageContainer -Name $StorageContainerName -Context $StorageAccount.Context -ErrorAction SilentlyContinue *>&1
80 |
81 | $ArtifactFilePaths = Get-ChildItem $ArtifactStagingDirectory -Recurse -File | ForEach-Object -Process {$_.FullName}
82 | foreach ($SourcePath in $ArtifactFilePaths) {
83 | Set-AzureStorageBlobContent -File $SourcePath -Blob $SourcePath.Substring($ArtifactStagingDirectory.length + 1) `
84 | -Container $StorageContainerName -Context $StorageAccount.Context -Force
85 | }
86 |
87 | # Generate a 4 hour SAS token for the artifacts location if one was not provided in the parameters file
88 | if ($OptionalParameters[$ArtifactsLocationSasTokenName] -eq $null) {
89 | $OptionalParameters[$ArtifactsLocationSasTokenName] = ConvertTo-SecureString -AsPlainText -Force `
90 | (New-AzureStorageContainerSASToken -Container $StorageContainerName -Context $StorageAccount.Context -Permission r -ExpiryTime (Get-Date).AddHours(4))
91 | }
92 | }
93 |
94 | # Create or update the resource group using the specified template file and template parameters file
95 | New-AzureRmResourceGroup -Name $ResourceGroupName -Location $ResourceGroupLocation -Verbose -Force
96 |
97 | if ($ValidateOnly) {
98 | $ErrorMessages = Format-ValidationOutput (Test-AzureRmResourceGroupDeployment -ResourceGroupName $ResourceGroupName `
99 | -TemplateFile $TemplateFile `
100 | -TemplateParameterFile $TemplateParametersFile `
101 | @OptionalParameters)
102 | if ($ErrorMessages) {
103 | Write-Output '', 'Validation returned the following errors:', @($ErrorMessages), '', 'Template is invalid.'
104 | }
105 | else {
106 | Write-Output '', 'Template is valid.'
107 | }
108 | }
109 | else {
110 | New-AzureRmResourceGroupDeployment -Name ((Get-ChildItem $TemplateFile).BaseName + '-' + ((Get-Date).ToUniversalTime()).ToString('MMdd-HHmm')) `
111 | -ResourceGroupName $ResourceGroupName `
112 | -TemplateFile $TemplateFile `
113 | -TemplateParameterFile $TemplateParametersFile `
114 | @OptionalParameters `
115 | -Force -Verbose `
116 | -ErrorVariable ErrorMessages
117 | if ($ErrorMessages) {
118 | Write-Output '', 'Template deployment returned the following errors:', @(@($ErrorMessages) | ForEach-Object { $_.Exception.Message.TrimEnd("`r`n") })
119 | }
120 | }
--------------------------------------------------------------------------------
/Deployment.targets:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Debug
5 | AnyCPU
6 | bin\$(Configuration)\
7 | false
8 | true
9 | false
10 | None
11 | obj\
12 | $(BaseIntermediateOutputPath)\
13 | $(BaseIntermediateOutputPath)$(Configuration)\
14 | $(IntermediateOutputPath)ProjectReferences
15 | $(ProjectReferencesOutputPath)\
16 | true
17 |
18 |
19 |
20 | false
21 | false
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 | Always
33 |
34 |
35 | Never
36 |
37 |
38 | false
39 | Build
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 | _GetDeploymentProjectContent;
48 | _CalculateContentOutputRelativePaths;
49 | _GetReferencedProjectsOutput;
50 | _CalculateArtifactStagingDirectory;
51 | _CopyOutputToArtifactStagingDirectory;
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 | Configuration=$(Configuration);Platform=$(Platform)
69 |
70 |
71 |
75 |
76 |
77 |
78 | $([System.IO.Path]::GetFileNameWithoutExtension('%(ProjectReference.Identity)'))
79 |
80 |
81 |
82 |
83 |
84 |
85 | $(OutDir)
86 | $(OutputPath)
87 | $(ArtifactStagingDirectory)\
88 | $(ArtifactStagingDirectory)staging\
89 | $(Build_StagingDirectory)
90 |
91 |
92 |
93 |
94 |
96 |
97 | <_OriginalIdentity>%(DeploymentProjectContentOutput.Identity)
98 | <_RelativePath>$(_OriginalIdentity.Replace('$(MSBuildProjectDirectory)', ''))
99 |
100 |
101 |
102 |
103 | $(_RelativePath)
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 | PrepareForRun
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # serverless-tutorial-deployment
2 | Part of the Channel 9 walkthrough on deploying serverless applications
3 |
4 | [Azure Function Code](https://github.com/jeffhollan/serverless-tutorial-functions)
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/azuredeploy.json:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jeffhollan/serverless-tutorial-deployment/c33ee3d684e4d9def993e8e09f1562d80f59248e/azuredeploy.json
--------------------------------------------------------------------------------
/azuredeploy.parameters.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#",
3 | "contentVersion": "1.0.0.0",
4 | "parameters": {
5 | "logicAppName": {
6 | "value": "csv-to-email"
7 | },
8 | "ftpserverAddress": {
9 | "value": "myserver"
10 | },
11 | "ftpuserName": {
12 | "value": "myFTP\\username"
13 | },
14 | "ftppassword": {
15 | "value": "somepassword"
16 | },
17 | "ftpserverPort": {
18 | "value": 21
19 | },
20 | "ftpisssl": {
21 | "value": true
22 | },
23 | "ftpisBinaryTransport": {
24 | "value": true
25 | },
26 | "ftpdisableCertificateValidation": {
27 | "value": true
28 | },
29 | "repoUrl": {
30 | "value": "https://github.com/jeffhollan/serverless-tutorial-functions.git"
31 | },
32 | "appName": {
33 | "value": "uniqueFunctionName"
34 | }
35 | }
36 | }
--------------------------------------------------------------------------------
/resource-group-template.deployproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 |
8 |
9 | Release
10 | AnyCPU
11 |
12 |
13 |
14 | e33f1d9e-63b1-4e89-a0dd-870d90bca52c
15 |
16 |
17 | Deployment
18 | 1.0
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 | False
30 |
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------