├── .github
└── pull_request_template.md
├── ARMHelper
├── ARMHelper.format.ps1xml
├── ARMHelper.psd1
├── ARMHelper.psm1
├── Private
│ ├── Get-ARMResource.ps1
│ ├── Get-ResourceProperty.ps1
│ └── Test-ARMAzureModule.ps1
└── Public
│ ├── Get-ARMDeploymentErrorMessage.ps1
│ ├── Test-ARMDeploymentResource.ps1
│ └── Test-ARMexistingResource.ps1
├── CHANGELOG.md
├── Devazure-pipeline.yml
├── LICENSE
├── README.md
├── Tests
├── AzureTesting
│ ├── Get-ARMDEploymentErrorMessage.tests.ps1
│ ├── StorageAccountBroken
│ │ ├── azuredeploy.json
│ │ └── azuredeploy.parameters.json
│ ├── StorageAccountFixed
│ │ ├── azuredeploy.json
│ │ └── azuredeploy.parameters.json
│ ├── StorageAccountGE
│ │ ├── azuredeploy.json
│ │ └── azuredeploy.parameters.json
│ ├── Test-ARMDeploymentResource.tests.ps1
│ ├── VirtualMachine
│ │ ├── azuredeploy.json
│ │ └── azuredeploy.parameters.json
│ └── pipelinetest.ps1
├── Basic_Module.tests.ps1
├── Get-ARMDeploymentErrorMessage.tests.ps1
├── GetHelp.tests.ps1
├── MockObjects
│ ├── ExistingResources.json
│ ├── ExistingResourcesDeleted.json
│ ├── ExistingResourcesdiffRG.json
│ ├── Logoutput.json
│ ├── NestedResult.json
│ ├── Result.json
│ ├── ResultComplete.json
│ ├── ResultCompleteaz.json
│ ├── Resultaz.json
│ └── azuredeploy.json
├── PSScriptAnalyzer.ps1
├── Test-ARMAzureModule.tests.ps1
├── Test-ARMDeploymentResource.tests.ps1
└── Test-ARMExistingResource.tests.ps1
├── azure-pipelines.yml
├── azure-pipelinesazuretemplate.yml
├── azure-pipelinestemplate.yml
└── docs
├── Get-ARMDeploymentErrorMessage.md
├── Test-ARMDeploymentResource.md
├── Test-ARMExistingResource.md
└── en-US
└── ArmHelper-help.xml
/.github/pull_request_template.md:
--------------------------------------------------------------------------------
1 | # Description
2 |
3 | description here
4 |
5 | ### Checklist
6 |
7 | - [ ] Issue referenced with #
8 | - [ ] Build for push succeeded
9 | - [ ] Changelog updated
10 | - [ ] Version in psd updated
11 | - [ ] Docs updated
12 |
--------------------------------------------------------------------------------
/ARMHelper/ARMHelper.format.ps1xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Default
6 |
7 | ArmHelper.Default
8 |
9 |
10 | TypeShort
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | Resource
19 |
20 |
21 | Name
22 |
23 |
24 | Type
25 |
26 |
27 | Location
28 |
29 |
30 | Mode
31 |
32 |
33 | Id
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 | ExistingResource
42 |
43 | ArmHelper.ExistingResource
44 |
45 |
46 |
47 |
48 | 50
49 |
50 |
51 | 50
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 | type
62 |
63 |
64 | name
65 |
66 |
67 | ResourcegroupName
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
--------------------------------------------------------------------------------
/ARMHelper/ARMHelper.psd1:
--------------------------------------------------------------------------------
1 | #
2 | # Module manifest for module 'Psado'
3 | #
4 | # Generated by: Barbara Forbes
5 | #
6 | # Generated on: 11/02/2019
7 | #
8 | @{
9 | # Script module or binary module file associated with this manifest.
10 | RootModule = 'ARMHelper.psm1'
11 |
12 | # Version number of this module.
13 | ModuleVersion = '0.6.3'
14 |
15 | # Supported PSEditions
16 | # CompatiblePSEditions = @()
17 |
18 | # ID used to uniquely identify this module
19 | GUID = '037c5c96-e20f-409c-8e42-5963294bb2b3'
20 |
21 | # Author of this module
22 | Author = 'Barbara Forbes'
23 |
24 | # Company or vendor of this module
25 | CompanyName = '4bes.nl'
26 |
27 | # Copyright statement for this module
28 | Copyright = '(c) Barbara Forbes. All rights reserved.'
29 |
30 | # Description of the functionality provided by this module
31 | Description = 'Functions to help with the deployment of ARM Templates.'
32 |
33 | # Minimum version of the PowerShell engine required by this module
34 | # PowerShellVersion = ''
35 |
36 | # Name of the PowerShell host required by this module
37 | # PowerShellHostName = ''
38 |
39 | # Minimum version of the PowerShell host required by this module
40 | # PowerShellHostVersion = ''
41 |
42 | # Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only.
43 | # DotNetFrameworkVersion = ''
44 |
45 | # Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only.
46 | # CLRVersion = ''
47 |
48 | # Processor architecture (None, X86, Amd64) required by this module
49 | # ProcessorArchitecture = ''
50 |
51 | # Modules that must be imported into the global environment prior to importing this module
52 | # RequiredModules = @()
53 |
54 | # Assemblies that must be loaded prior to importing this module
55 | # RequiredAssemblies = @()
56 |
57 | # Script files (.ps1) that are run in the caller's environment prior to importing this module.
58 | # ScriptsToProcess = @()
59 |
60 | # Type files (.ps1xml) to be loaded when importing this module
61 | # TypesToProcess = @()
62 |
63 | # Format files (.ps1xml) to be loaded when importing this module
64 | FormatsToProcess = @('ARMHelper.format.ps1xml')
65 |
66 | # Modules to import as nested modules of the module specified in RootModule/ModuleToProcess
67 | # NestedModules = @()
68 |
69 | # Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export.
70 | FunctionsToExport = @(
71 | 'Test-ARMDeploymentResource'
72 | 'Get-ARMDeploymentErrorMessage'
73 | 'Test-ARMexistingResource'
74 | )
75 |
76 | # Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export.
77 | CmdletsToExport = @()
78 |
79 | # Variables to export from this module
80 | VariablesToExport = @()
81 |
82 | # Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export.
83 | AliasesToExport = @()
84 |
85 | # DSC resources to export from this module
86 | # DscResourcesToExport = @()
87 |
88 | # List of all modules packaged with this module
89 | # ModuleList = @()
90 |
91 | # List of all files packaged with this module
92 | # FileList = @()
93 |
94 | # Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell.
95 | PrivateData = @{
96 |
97 | PSData = @{
98 |
99 | # Tags applied to this module. These help with module discovery in online galleries.
100 | Tags = @('Windows','Linux','MacOS','ARM')
101 |
102 | # A URL to the license for this module.
103 | # LicenseUri = ''
104 |
105 | # A URL to the main website for this project.
106 | ProjectUri = 'https://github.com/Ba4bes/ARMHelper'
107 |
108 | # A URL to an icon representing this module.
109 | # IconUri = ''
110 |
111 | # ReleaseNotes of this module
112 | # ReleaseNotes = ''
113 |
114 | } # End of PSData hashtable
115 |
116 | } # End of PrivateData hashtable
117 |
118 | # HelpInfo URI of this module
119 | # HelpInfoURI = ''
120 |
121 | # Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix.
122 | # DefaultCommandPrefix = ''
123 |
124 | }
125 |
--------------------------------------------------------------------------------
/ARMHelper/ARMHelper.psm1:
--------------------------------------------------------------------------------
1 | #Get public and private function definition files.
2 | $Private = @( Get-ChildItem -Path $PSScriptRoot\Private\*.ps1 -ErrorAction SilentlyContinue )
3 | $Public = @( Get-ChildItem -Path $PSScriptRoot\Public\*.ps1 -ErrorAction SilentlyContinue )
4 | #$Public = @( Get-ChildItem -Path 'C:\Scripts\GIT\Gitlab - Private\DagelijksGehenk\Armado\*.ps1' -ErrorAction SilentlyContinue )
5 |
6 |
7 | $Scripts = $Private + $Public
8 |
9 | #Dot source the files
10 | Foreach ($import in $Scripts) {
11 | Try {
12 | . $import.fullname
13 | }
14 | Catch {
15 | Write-Error -Message "Failed to import function $($import.fullname): $_"
16 | }
17 | }
--------------------------------------------------------------------------------
/ARMHelper/Private/Get-ARMResource.ps1:
--------------------------------------------------------------------------------
1 |
2 | Function Get-ARMResource {
3 | [CmdletBinding(DefaultParameterSetName = "__AllParameterSets")]
4 | Param(
5 | [Parameter(
6 | Position = 1,
7 | Mandatory = $true,
8 | ParameterSetName = "__AllParameterSets"
9 | )]
10 | [ValidateNotNullorEmpty()]
11 | [string] $ResourceGroupName,
12 |
13 | [Parameter(
14 | Position = 2,
15 | Mandatory = $true,
16 | ParameterSetName = "__AllParameterSets"
17 | )]
18 | [ValidateNotNullorEmpty()]
19 | [string] $TemplateFile,
20 |
21 | [Parameter(
22 | ParameterSetName = 'TemplateParameterFile',
23 | Mandatory = $true
24 | )]
25 | [string] $TemplateParameterFile,
26 |
27 | [Parameter(
28 | ParameterSetName = 'TemplateParameterObject',
29 | Mandatory = $true
30 | )]
31 | [hashtable] $TemplateParameterObject,
32 |
33 | [parameter (
34 | ParameterSetName = "__AllParameterSets",
35 | Mandatory = $false
36 | )]
37 | [ValidateSet("Incremental", "Complete")]
38 | [string] $Mode = "Incremental"
39 | )
40 | DynamicParam {
41 | if ($TemplateFile) {
42 | #create a new ParameterAttribute Object
43 | $OverRideParameter = New-Object System.Management.Automation.ParameterAttribute
44 | $OverRideParameter.Mandatory = $false
45 | #create an attributecollection object for the attribute we just created.
46 | $AttributeCollection = new-object System.Collections.ObjectModel.Collection[System.Attribute]
47 | $AttributeCollection.Add($OverRideParameter)
48 | $paramDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
49 | $Parameters = (Get-Content $TemplateFile | ConvertFrom-Json).parameters
50 | $ParameterValues = $parameters | Get-Member -MemberType NoteProperty
51 | ForEach ($Param in $ParameterValues) {
52 | $Name = $Param.Name
53 | $type = ($Parameters.$Name).type
54 | #add our paramater specifying the attribute collection
55 | $ExtraParam = New-Object System.Management.Automation.RuntimeDefinedParameter($Param.Name, ($Type -as [type]), $attributeCollection)
56 | #expose the name of our parameter
57 |
58 | $paramDictionary.Add($Param.Name, $ExtraParam)
59 | }
60 | return $paramDictionary
61 | }
62 | }
63 | process {
64 | #set variables
65 | $Parameters = @{
66 | ResourceGroupName = $ResourceGroupName
67 | TemplateFile = $TemplateFile
68 | Mode = $Mode
69 | }
70 | if (-not[string]::IsNullOrEmpty($TemplateParameterFile) ) {
71 | $Parameters.Add("TemplateParameterFile", $TemplateParameterFile)
72 | }
73 | if (-not[string]::IsNullOrEmpty($TemplateParameterObject) ) {
74 | $Parameters.Add("TemplateParameterObject", $TemplateParameterObject)
75 | }
76 | $CustomParameters = (Get-Content $TemplateFile | ConvertFrom-Json).parameters
77 | $CustomParameterValues = $Customparameters | Get-Member -MemberType NoteProperty
78 | foreach ($param in $CustomParameterValues) {
79 | $paramname = $param.Name
80 | if (-not[string]::IsNullOrEmpty($PSBoundParameters.$paramname)) {
81 | $Key = $paramname
82 | $Value = $PSBoundParameters.$paramname
83 | $Parameters.Add($Key, $Value)
84 | }
85 | }
86 | $Output = $null
87 | #set debugpreference to continue so the cmdlet runs with more output
88 | $Module = Test-ARMAzureModule
89 | $oldDebugPreference = $DebugPreference
90 | $DebugPreference = "Continue"
91 |
92 |
93 | if ($Module -eq "Az") {
94 | $Output = Test-AzResourceGroupDeployment @parameters 5>&1 -ErrorAction Stop
95 | }
96 | elseif ($Module -eq "AzureRM") {
97 | $Output = Test-AzureRmResourceGroupDeployment @parameters 5>&1 -ErrorAction Stop
98 |
99 | }
100 | else {
101 | Throw "Something went wrong, No AzureRM of AZ module found"
102 | }
103 | #Set DebugPreference back to original setting
104 | $DebugPreference = $oldDebugPreference
105 | if ([string]::IsNullOrEmpty($Output)) {
106 | Throw "Something went wrong, Test-AzureRmResourceGroupDeployment didn't give output"
107 | }
108 | #Grap the specific part of the output that tells you about the deployed Resources
109 | $Response = $Output | Where-Object { $_.Message -like "*http response*" }
110 | #get the jsonpart en convert it to work with it.
111 | $Result = (($Response -split "Body:")[1] | ConvertFrom-Json).Properties
112 |
113 | $Result
114 | }
115 | }
--------------------------------------------------------------------------------
/ARMHelper/Private/Get-ResourceProperty.ps1:
--------------------------------------------------------------------------------
1 |
2 | <#
3 | .SYNOPSIS
4 | Returns a HashTable with all properties of an object, including nested properties
5 |
6 | .DESCRIPTION
7 | This function goes through all properties and puts them on one level.
8 |
9 | .PARAMETER Object
10 | Mandatory - The object to list properties of
11 |
12 | .PARAMETER MaxLevels
13 | Specifies how many levels deep to list
14 |
15 | .PARAMETER PathName
16 | Specifies the path name to use as the root. If not specified, all properties will start with "."
17 |
18 | .PARAMETER Level
19 | Specifies which level the function is currently processing. Should not be used manually.
20 |
21 | .EXAMPLE
22 | Get-ResourceProperty -Object $Resource
23 |
24 | .NOTES
25 | This is a modification of a script by KevinD.
26 | http://stackoverflow.com/users/1298933/kevind
27 | Source: https://stackoverflow.com/questions/22388226/powershell-script-delete-first-character-in-output
28 | Modified by: Barbara Forbes
29 | Module: ARMHelper
30 | https://4bes.nl
31 | @Ba4bes
32 | #>
33 | function Get-ResourceProperty {
34 | [CmdletBinding()]
35 | param (
36 | [Parameter(Position = 1, Mandatory = $true)]
37 | [psobject] $Object,
38 | [Parameter()]
39 | [int] $MaxLevels = 10,
40 | [Parameter()]
41 | [string] $PathName = "",
42 | [Parameter()]
43 | [int] $Level = 0
44 | )
45 | #Initialize an array to store properties
46 | $Props = @()
47 | if ($Level -eq 0) {
48 | $PropertiesReadable = @{ }
49 | }
50 | $RootProperties = $Object | Get-Member -MemberType NoteProperty
51 |
52 | # Make sure we're not exceeding the MaxLevels
53 | if ($Level -lt $MaxLevels) {
54 |
55 | # Properties of the following types don't need another loop
56 | $TypesToWrite = "System.Boolean", "System.String", "System.Int32", "System.Char"
57 |
58 | #Loop through the root properties
59 | foreach ($RootProperty in $RootProperties) {
60 |
61 | #Base name of property
62 | $Propname = $RootProperty.Name
63 |
64 | #Object to process
65 | $PropertyObject = $($Object.$Propname)
66 | if ($Null -eq $PropertyObject) {
67 | Continue
68 | }
69 | # Get the type, and only recurse into it if it is not one of our excluded types
70 | $Type = ($PropertyObject.GetType()).tostring()
71 | $Array = ($PropertyObject.GetType()).BaseType.tostring()
72 |
73 | # If it's an array, go through each object
74 | if ($Array -eq "System.Array") {
75 | foreach ($PropObject in $PropertyObject) {
76 | $Key = $PropObject.Name
77 | $Value = $PropObject.Value
78 | if ([string]::IsNullOrEmpty($key)) {
79 | continue
80 | }
81 | if ([string]::IsNullOrEmpty($Value)) {
82 | $Members = ($PropObject | get-member -Type NoteProperty | Where-Object { $_.Name -ne "Name" }).Name
83 | $Value = $PropObject.$Members
84 | }
85 | if ($PropertiesReadable.$Key) {
86 | $Path = $PathName.Replace(".properties", "")
87 | $Key = "$Path.$($PropObject.Name)"
88 | }
89 | if ($SecureParameters -contains $Key) {
90 | # This is a bit of a workaround to avoid a plaintext securestring
91 | # The only thing that's better is that this doesn't trigger PSScriptAnalyzer :')
92 | $SecValue = New-Object SecureString
93 | [char[]]($Value) | ForEach-Object { $SecValue.AppendChar($_) }
94 | $Value = $SecValue
95 | }
96 | $PropertiesReadable.add($Key, $Value)
97 | Continue
98 | }
99 | }
100 | #If $TypesToWrite containt the type, write results to hashtable
101 | Elseif (($TypesToWrite.Contains($Type) ) ) {
102 |
103 | $Props += "$PathName.$($RootProperty.Name)"
104 | $Key = $RootProperty.Name
105 | $Value = $PropertyObject
106 |
107 | # Add tags for readability
108 | if ($PathName -like "*Tags*") {
109 | $Key = "Tags: $($RootProperty.Name)"
110 |
111 | }
112 | if ($PropertiesReadable.$Key) {
113 | $Path = $PathName.Replace(".properties", "")
114 | $Key = "$Path.$($RootProperty.Name)"
115 | }
116 | if ($SecureParameters -contains $Key) {
117 | # This is a bit of a workaround to avoid a plaintext securestring
118 | # The only thing that's better is that this doesn't trigger PSScriptAnalyzer :')
119 | $SecValue = New-Object SecureString
120 | [char[]]($Value) | ForEach-Object { $SecValue.AppendChar($_) }
121 | $Value = $SecValue
122 | }
123 | $PropertiesReadable.add($Key, $Value)
124 | }
125 | # If $TypesToWrite does not contain the type, recurse.
126 | Elseif (-not($TypesToWrite.Contains($Type) ) ) {
127 | #Create a new path to the property
128 | $ChildPathName = "$PathName.$Propname"
129 |
130 | # Make sure it's not null, then recurse, incrementing $Level
131 | if ($Null -ne $PropertyObject) {
132 | $Props += Get-ResourceProperty -Object $PropertyObject -PathName $ChildPathName -Level ($Level + 1) -MaxLevels $MaxLevels
133 | }
134 | }
135 | }
136 | }
137 | $PropertiesReadable
138 | }
139 |
--------------------------------------------------------------------------------
/ARMHelper/Private/Test-ARMAzureModule.ps1:
--------------------------------------------------------------------------------
1 | Function Test-ARMAzureModule {
2 |
3 | $Module = $null
4 | $AzLoaded = Get-Module -Name Az.*
5 | $AzureRMLoaded = Get-Module -Name AzureRM.*
6 | if (-not[string]::IsNullOrEmpty($AzLoaded)) {
7 | $Module = "Az"
8 | }
9 | elseif (-not[string]::IsNullOrEmpty($AzureRMLoaded)) {
10 | $Module = "AzureRM"
11 | }
12 | Else {
13 | $Az = Get-InstalledModule AZ -ErrorAction SilentlyContinue
14 | $AzureRM = get-installedModule AzureRM -ErrorAction SilentlyContinue
15 | If (-not[string]::IsNullOrEmpty($Az)) {
16 | $Module = "Az"
17 | }
18 | Elseif (-not[string]::IsNullOrEmpty($AzureRM)) {
19 | $Module = "AzureRM"
20 | }
21 | }
22 | Write-Verbose "Az is found"
23 | try {
24 | if ($Module -eq "Az") {
25 | $null = Get-AzContext
26 | }
27 | elseif ($Module -eq "AzureRM") {
28 | $null = Get-AzureRMContext
29 | }
30 | }
31 | Catch {
32 | Throw "No connection with Az has been found. Please Connect."
33 | }
34 |
35 | if ([string]::IsNullOrEmpty($Module)) {
36 | Throw "neither AZ of AzureRM could be loaded"
37 | }
38 | $Module
39 | }
--------------------------------------------------------------------------------
/ARMHelper/Public/Get-ARMDeploymentErrorMessage.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | .SYNOPSIS
3 | Tests an azure deployment for errors, Use the azure Logs if a generic message is given.
4 |
5 | .DESCRIPTION
6 | This function uses Test-AzureRmResourceGroupDeployment or Test-AZResourcegroupDeployment. There is a specific errormessage that's very generic.
7 | If this is the output, the correct errormessage is retrieved from the Azurelog.
8 |
9 | .PARAMETER ResourceGroupName
10 | The resourcegroup where the resources would be deployed to. This resourcegroup needs to exist.
11 |
12 | .PARAMETER TemplateFile
13 | The path to the templatefile
14 |
15 | .PARAMETER TemplateParameterFile
16 | The path to the parameterfile, optional
17 |
18 | .PARAMETER TemplateParameterObject
19 | A Hasbtable with parameters, optional
20 |
21 | .PARAMETER Pipeline
22 | Use this parameter if this script is used in a CICDpipeline. It will make the step fail.
23 | This parameter is replaced by ThrowOnError and will be removed in a later release!
24 |
25 | .PARAMETER ThrowOnError
26 | This Switch will make the cmdlet throw when the deployment is incorrect. This can be useful in a pipeline, it will make the task fail.
27 |
28 | .EXAMPLE
29 | Get-ARMDeploymentErrorMessage -ResourceGroupName ArmTest -TemplateFile .\azuredeploy.json -TemplateParameterFile .\azuredeploy.parameters.json
30 |
31 | --------
32 | the output is a generic error message. The log is searched for a more clear errormessageGeneral Error. Find info below:
33 | ErrorCode: InvalidDomainNameLabel
34 | Errormessage: The domain name label LABexample is invalid. It must conform to the following regular expression: ^[a-z][a-z0-9-]{1,61}[a-z0-9]$.
35 |
36 | .EXAMPLE
37 | Get-ARMDeploymentErrorMessage Armtesting .\VM01\azuredeploy.json -TemplateParameterObject $Parameters
38 |
39 | --------
40 | deployment is correct
41 |
42 | .NOTES
43 | Dynamic Parameters like in the orginal Test-AzResourcegroupDeployment-cmdlet are supported
44 | Author: Barbara Forbes
45 | Module: ARMHelper
46 | https://4bes.nl
47 | @Ba4bes
48 | #>
49 | function Get-ARMDeploymentErrorMessage {
50 | [CmdletBinding(DefaultParameterSetName = "__AllParameterSets")]
51 | Param(
52 | [Parameter(
53 | Position = 1,
54 | Mandatory = $true,
55 | ParameterSetName = "__AllParameterSets"
56 | )]
57 | [ValidateNotNullorEmpty()]
58 | [string] $ResourceGroupName,
59 |
60 | [Parameter(
61 | Position = 2,
62 | Mandatory = $true,
63 | ParameterSetName = "__AllParameterSets"
64 | )]
65 | [ValidateNotNullorEmpty()]
66 | [string] $TemplateFile,
67 |
68 | [Parameter(
69 | ParameterSetName = 'TemplateParameterFile',
70 | Mandatory = $true
71 | )]
72 | [string] $TemplateParameterFile,
73 |
74 | [Parameter(
75 | ParameterSetName = 'TemplateParameterObject',
76 | Mandatory = $true
77 | )]
78 | [hashtable] $TemplateParameterObject,
79 | [Parameter(
80 | ParameterSetName = "__AllParameterSets"
81 | )]
82 | [switch] $Pipeline,
83 |
84 | [Parameter(
85 | ParameterSetName = "__AllParameterSets"
86 | )]
87 | [switch] $ThrowOnError
88 | )
89 | DynamicParam {
90 | if ($TemplateFile) {
91 | #create a new ParameterAttribute Object
92 | $OverRideParameter = New-Object System.Management.Automation.ParameterAttribute
93 | $OverRideParameter.Mandatory = $false
94 | #create an attributecollection object for the attribute we just created.
95 | $AttributeCollection = new-object System.Collections.ObjectModel.Collection[System.Attribute]
96 | $AttributeCollection.Add($OverRideParameter)
97 | $paramDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
98 | $Parameters = (Get-Content $TemplateFile | ConvertFrom-Json).parameters
99 | $ParameterValues = $parameters | Get-Member -MemberType NoteProperty
100 | ForEach ($Param in $ParameterValues) {
101 | $Name = $Param.Name
102 | $type = ($Parameters.$Name).type
103 | #add our paramater specifying the attribute collection
104 | $ExtraParam = New-Object System.Management.Automation.RuntimeDefinedParameter($Param.Name, ($Type -as [type]), $attributeCollection)
105 |
106 | #expose the name of our parameter
107 |
108 | $paramDictionary.Add($Param.Name, $ExtraParam)
109 | }
110 | return $paramDictionary
111 | }
112 | }
113 | process {
114 |
115 |
116 | if ($Pipeline) {
117 | Write-Warning "This parameter will be removed in the next release. Please use -ThrowOnError as an replacement"
118 | }
119 |
120 | #set variables
121 | $Output = $null
122 | $DetailedError = $null
123 | $Parameters = @{
124 | ResourceGroupName = $ResourceGroupName
125 | TemplateFile = $TemplateFile
126 | }
127 | if (-not[string]::IsNullOrEmpty($TemplateParameterFile) ) {
128 | $Parameters.Add("TemplateParameterFile", $TemplateParameterFile)
129 | }
130 | if (-not[string]::IsNullOrEmpty($TemplateParameterObject) ) {
131 | $Parameters.Add("TemplateParameterObject", $TemplateParameterObject)
132 | }
133 |
134 | $CustomParameters = (Get-Content $TemplateFile | ConvertFrom-Json).parameters
135 | $CustomParameterValues = $Customparameters | Get-Member -MemberType NoteProperty
136 | foreach ($param in $CustomParameterValues) {
137 | $paramname = $param.Name
138 | if (-not[string]::IsNullOrEmpty($PSBoundParameters.$paramname)) {
139 | $Key = $paramname
140 | $Value = $PSBoundParameters.$paramname
141 | $Parameters.Add($Key, $Value)
142 | }
143 | }
144 |
145 | #Get the AzureModule that's being used
146 | $Module = Test-ARMAzureModule
147 | try {
148 | if ($Module -eq "Az") {
149 | $Output = Test-AzResourceGroupDeployment @parameters
150 | }
151 | elseif ($Module -eq "AzureRM") {
152 | $Output = Test-AzureRmResourceGroupDeployment @parameters
153 | }
154 | else {
155 | Throw "Something went wrong, No AzureRM of AZ module found"
156 | }
157 | }
158 | catch {
159 | throw "Could not test deployment because of following error $_"
160 | }
161 |
162 | #Check for a specific output. This output is a very generic error-message.
163 | #So this script looks for the more clear errormessage in the AzureLogs.
164 | if ($Output.Message -like "*s not valid according to the validation procedure*") {
165 | Write-Output "the output is a generic error message. The log is searched for a more clear errormessage"
166 | #use regex to find the ID of the log
167 | $Regex = '[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}'
168 | $IDs = $Output.Message | Select-String $Regex -AllMatches
169 | $trackingID = $IDs.Matches.Value | Select-Object -Last 1
170 | $MaxTries = 0
171 | do {
172 | Start-Sleep 30
173 | Write-Output "The log is searched."
174 |
175 | if ($Module -eq "Az") {
176 | $LogContent = (Get-AzLog -CorrelationId $trackingID -WarningAction ignore).Properties.Content
177 | }
178 | elseif ($Module -eq "AzureRM") {
179 | $LogContent = (Get-AzureRmLog -CorrelationId $trackingID -WarningAction ignore).Properties.Content
180 | }
181 | else {
182 | Throw "Something went wrong, No AzureRM of AZ module found"
183 | }
184 | $MaxTries ++
185 | } while ($null -eq $LogContent -and $maxtries -le 10)
186 |
187 | if ($maxtries -gt 10 ) {
188 | Throw "Can't get Azure Log Entry. Please check the log manually in the portal."
189 | }
190 | if ([string]::IsNullOrEmpty($LogContent)) {
191 | Throw "Something went wrong when collecting the log. Please try again"
192 | }
193 |
194 | $DetailedError = ($LogContent[0].statusMessage)
195 | $TestError = $DetailedError | ConvertFrom-Json
196 |
197 | # The structure for the Log content is inconsistent. This is why a few tricks are used to get the right property
198 |
199 | # Get a list of all properties
200 | $ErrorProperties = Get-ResourceProperty -Object $TestError
201 |
202 | #List of properties that are not relevant
203 | $NotRelevant = @(
204 | '.*PreflightValidationCheck*',
205 | '.*InvalidTemplateDeployment.*'
206 | '.*Preflight validation failed.*'
207 | '.*The template deployment.*'
208 | )
209 |
210 | foreach ($ErrorProperty in $ErrorProperties.GetEnumerator()) {
211 | if ($ErrorProperty.Key -like "*code*") {
212 |
213 | if ( $ErrorProperty.Value -notmatch ($NotRelevant -join "|") ) {
214 | $ErrorCode = $ErrorProperty.Value
215 | }
216 | }
217 | if ($ErrorProperty.Key -like "*message*") {
218 | if ( $ErrorProperty.Value -notmatch ($NotRelevant -join "|")) {
219 | $ErrorMessage = $ErrorProperty.Value
220 | }
221 | }
222 | }
223 |
224 | if (([string]::IsNullOrEmpty($ErrorCode)) -or ([string]::IsNullOrEmpty($ErrorMessage))) {
225 | Throw "Script could not get the correct error message. Please try again"
226 | }
227 | }
228 |
229 | if (-not [string]::IsNullOrEmpty($Output) ) {
230 | #check if DetailedError has been used. if it is, return the value
231 | if (-not[string]::IsNullOrEmpty($DetailedError)) {
232 | Write-Output "General Error. Find info below:"
233 | Write-Output "ErrorCode: $ErrorCode"
234 | Write-Output "Errormessage: $ErrorMessage"
235 | }
236 | #if not, output the original message
237 | if ([string]::IsNullOrEmpty($DetailedError)) {
238 | Write-Output "Error, Find info below:"
239 | Write-Output $Output.Message
240 | }
241 | #exit code 1 is for Azure DevOps to stop the build in failed state. locally it just stops the script
242 | if ($Pipeline) {
243 | [Environment]::Exit(1)
244 | }
245 | if ($ThrowOnError) {
246 | Throw "Deployment is incorrect"
247 | }
248 | }
249 | else {
250 | Write-Output "deployment is correct"
251 | }
252 | }
253 |
254 | }
255 |
--------------------------------------------------------------------------------
/ARMHelper/Public/Test-ARMDeploymentResource.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | .SYNOPSIS
3 | Gives output that shows all resources that would be deployed by an ARMtemplate
4 |
5 | .DESCRIPTION
6 | When you enter a ARM template and a parameter file, this function will show what would be deployed
7 | To do this, it used the debug output of Test-AzureRmResourceGroupDeployment or Test-AzResourceGroupDeployment.
8 | A list of all the resources is provided with the most important properties.
9 | Some resources have seperated functions to structure the output.
10 | If no function is available, a generic output will be given.
11 |
12 | .PARAMETER ResourceGroup
13 | The resourcegroup where the resources would be deployed to. If it doesn't exist, it will be created
14 |
15 | .PARAMETER TemplateFile
16 | The path to the templatefile
17 |
18 | .PARAMETER TemplateParameterFile
19 | The path to the parameterfile, optional
20 |
21 | .PARAMETER TemplateParameterObject
22 | A Hasbtable with parameters, optional
23 |
24 | .EXAMPLE
25 | Test-ARMDeploymentResource -ResourceGroupName Armtest -TemplateFile .\azuredeploy.json -TemplateParameterFile .\azuredeploy.parameters.json
26 |
27 | --------
28 | Resource : storageAccounts
29 | Name : armsta12356
30 | Type : Microsoft.Storage/storageAccounts
31 | Location : westeurope
32 | mode : Incremental
33 | ID : /subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/arm/providers/Microsoft.Storage/storageAccounts/armsta12356
34 |
35 | .EXAMPLE
36 | Test-ARMDeploymentResource armtesting .\azuredeploy.json -TemplateParameterObject $parameters | select *
37 |
38 | --------
39 | Resource : storageAccounts
40 | Name : armsta12356
41 | Type : Microsoft.Storage/storageAccounts
42 | ID : /subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/armtesting/providers/Microsoft.Storage/storageAccounts/armsta12356
43 | Location : westeurope
44 | Tags: ARMcreated : True
45 | accountType : Standard_LRS
46 | apiVersion : 2015-06-15
47 | Tags: displayName : armsta12356
48 | mode : Incremental
49 |
50 | .NOTES
51 | Dynamic Parameters like in the orginal Test-AzResourcegroupDeployment-cmdlet are supported
52 | Script can be used in a CICD pipeline
53 | Author: Barbara Forbes
54 | Module: ARMHelper
55 | https://4bes.nl
56 | @Ba4bes
57 | Source for more output: #Source https://blog.mexia.com.au/testing-arm-templates-with-pester
58 | #>
59 | function Test-ARMDeploymentResource {
60 | [CmdletBinding(DefaultParameterSetName = "__AllParameterSets")]
61 | Param(
62 | [Parameter(
63 | Position = 1,
64 | Mandatory = $true,
65 | ParameterSetName = "__AllParameterSets"
66 | )]
67 | [ValidateNotNullorEmpty()]
68 | [string] $ResourceGroupName,
69 |
70 | [Parameter(
71 | Position = 2,
72 | Mandatory = $true,
73 | ParameterSetName = "__AllParameterSets"
74 | )]
75 | [ValidateNotNullorEmpty()]
76 | [string] $TemplateFile,
77 |
78 | [Parameter(
79 | ParameterSetName = 'TemplateParameterFile',
80 | Mandatory = $true
81 | )]
82 | [string] $TemplateParameterFile,
83 |
84 | [Parameter(
85 | ParameterSetName = 'TemplateParameterObject',
86 | Mandatory = $true
87 | )]
88 | [hashtable] $TemplateParameterObject,
89 |
90 | [parameter (
91 | ParameterSetName = "__AllParameterSets",
92 | Mandatory = $false
93 | )]
94 | [ValidateSet("Incremental", "Complete")]
95 | [string] $Mode = "Incremental"
96 | )
97 | DynamicParam {
98 | if ($TemplateFile) {
99 | #create a new ParameterAttribute Object
100 | $OverRideParameter = New-Object System.Management.Automation.ParameterAttribute
101 | $OverRideParameter.Mandatory = $false
102 | #create an attributecollection object for the attribute we just created.
103 | $AttributeCollection = new-object System.Collections.ObjectModel.Collection[System.Attribute]
104 | $AttributeCollection.Add($OverRideParameter)
105 | $paramDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
106 | $Parameters = (Get-Content $TemplateFile | ConvertFrom-Json).parameters
107 | $ParameterValues = $parameters | Get-Member -MemberType NoteProperty
108 | ForEach ($Param in $ParameterValues) {
109 | $Name = $Param.Name
110 | $type = ($Parameters.$Name).type
111 | #add our paramater specifying the attribute collection
112 | $ExtraParam = New-Object System.Management.Automation.RuntimeDefinedParameter($Param.Name, ($Type -as [type]), $attributeCollection)
113 |
114 | #expose the name of our parameter
115 |
116 | $paramDictionary.Add($Param.Name, $ExtraParam)
117 | }
118 | return $paramDictionary
119 | }
120 | }
121 | process {
122 |
123 | $Parameters = @{
124 | ResourceGroupName = $ResourceGroupName
125 | TemplateFile = $TemplateFile
126 | Mode = $Mode
127 | }
128 | if (-not[string]::IsNullOrEmpty($TemplateParameterFile) ) {
129 | $Parameters.Add("TemplateParameterFile", $TemplateParameterFile)
130 | }
131 | if (-not[string]::IsNullOrEmpty($TemplateParameterObject) ) {
132 | $Parameters.Add("TemplateParameterObject", $TemplateParameterObject)
133 | }
134 | $CustomParameters = (Get-Content $TemplateFile | ConvertFrom-Json).parameters
135 | $CustomParameterValues = $Customparameters | Get-Member -MemberType NoteProperty
136 | foreach ($param in $CustomParameterValues) {
137 | $paramname = $param.Name
138 | if (-not[string]::IsNullOrEmpty($PSBoundParameters.$paramname)) {
139 | $Key = $paramname
140 | $Value = $PSBoundParameters.$paramname
141 | $Parameters.Add($Key, $Value)
142 | }
143 | }
144 |
145 | $Result = Get-ARMResource @Parameters
146 | if ([string]::IsNullOrEmpty($Result.Mode)) {
147 | Throw "Something is wrong with the output, no resources found. Please check your deployment with Get-ARMdeploymentErrorMessage"
148 | }
149 |
150 | # A list of securestrings is created to mask the output at a later time
151 | $Resultparameters = ($Result.parameters) | get-member -MemberType NoteProperty
152 | $SecureParameters = [System.Collections.Generic.List[string]]::new()
153 |
154 | Foreach ($parameter in $Resultparameters) {
155 | $Type = $result.parameters.$($parameter.Name).Type
156 | If ($Type -eq "SecureString") {
157 | $SecureParameters.Add($Parameter.Name)
158 | }
159 | }
160 |
161 | $ValidatedResources = $Result.ValidatedResources
162 | # Check the module version used, as AZ has limited output
163 | $Module = Test-ARMAzureModule
164 | if ($Module -eq "Az"){
165 | Write-Warning "The AZ-module is used. This limits the results of this cmdlet. `n
166 | To get full results, consider temporary switching to the AzureRM-module"
167 | }
168 |
169 | #go through each deployed Resource
170 | foreach ($Resource in $ValidatedResources) {
171 | if ([string]::IsNullOrEmpty($Resource.Type) ) {
172 | $Resourceparts = $Resource.Id.Split('/')
173 | $ResourceName = $Resourceparts[-1]
174 | $ResourceType = $Resourceparts[-3] + "/" + $Resourceparts[-2]
175 | $ResourceTypeshort = $Resourceparts[-2]
176 | $ResourceReadable = [PSCustomObject]@{
177 | Resource = $ResourceTypeShort
178 | Name = $ResourceName
179 | Type = $Resourcetype
180 | ID = $Resource.id
181 | }
182 | }
183 | else {
184 |
185 | $ResourceTypeShort = $($Resource.type.Split("/")[-1])
186 |
187 | $ResourceReadable = [PSCustomObject] @{
188 | Resource = $ResourceTypeShort
189 | Name = $Resource.name
190 | Type = $Resource.type
191 | ID = $Resource.id
192 | Location = $Resource.location
193 | }
194 | $PropertiesReadable = Get-ResourceProperty -Object $Resource
195 |
196 | foreach ($Property in $PropertiesReadable.keys) {
197 | $ResourceReadable | Add-Member -MemberType NoteProperty -Name $Property -Value ($PropertiesReadable.$Property) -ErrorAction SilentlyContinue
198 | }
199 | #Add mode when it is not defined
200 | if ([string]::IsNullOrEmpty($ResourceReadable.mode)) {
201 | $ResourceReadable | Add-Member -MemberType NoteProperty -Name "mode" -Value ($Result.mode) -ErrorAction SilentlyContinue
202 | }
203 | $ResourceReadable.PSObject.TypeNames.Insert(0, 'ARMHelper.Default')
204 | }
205 | $ResourceReadable
206 | }
207 | }
208 | }
209 |
210 |
211 |
--------------------------------------------------------------------------------
/ARMHelper/Public/Test-ARMexistingResource.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | .SYNOPSIS
3 | Show if resource that are set to be deployed already exist
4 |
5 | .DESCRIPTION
6 | This function uses Test-AzureRmResourceGroupDeployment or Test-AzResourceGroupDeployment with debug output to find out what resources are deployed.
7 | After that, it checks if those resources exist in Azure.
8 | It will output the results when using complete mode or incremental mode (depending on the ARM template)
9 |
10 | .PARAMETER ResourceGroupName
11 | The resourcegroup where the resources would be deployed to. This resourcegroup needs to exist.
12 |
13 | .PARAMETER TemplateFile
14 | The path to the deploymentfile
15 |
16 | .PARAMETER TemplateParameterFile
17 | The path to the parameterfile
18 |
19 | .PARAMETER Mode
20 | The mode in which the deployment will run. Choose between Incremental or Complete.
21 | Defaults to incremental.
22 |
23 | .PARAMETER ThrowWhenRemoving
24 | This switch makes the function throw when a resources would be overwritten or deleted. This can be useful for use in a pipeline.
25 |
26 | .EXAMPLE
27 | Test-ARMexistingResource -ResourceGroupName ArmTest -TemplateFile .\azuredeploy.json -TemplateParameterFile .\azuredeploy.parameters.json
28 |
29 | --------
30 | The following resources exist. Mode is set to incremental. New properties might be added:
31 |
32 | type name Current ResourcegroupName
33 | ---- ---- -------------------------
34 | Microsoft.Storage/storageAccounts armsta armtest
35 |
36 | .NOTES
37 | Dynamic Parameters like in the orginal Test-AzResourcegroupDeployment-cmdlet are supported
38 | Author: Barbara Forbes
39 | Module: ARMHelper
40 | https://4bes.nl
41 | @Ba4bes
42 | #>
43 | Function Test-ARMExistingResource {
44 | [CmdletBinding(DefaultParameterSetName = "__AllParameterSets")]
45 | Param(
46 | [Parameter(
47 | Position = 1,
48 | Mandatory = $true,
49 | ParameterSetName = "__AllParameterSets"
50 | )]
51 | [ValidateNotNullorEmpty()]
52 | [string] $ResourceGroupName,
53 |
54 | [Parameter(
55 | Position = 2,
56 | Mandatory = $true,
57 | ParameterSetName = "__AllParameterSets"
58 | )]
59 | [ValidateNotNullorEmpty()]
60 | [string] $TemplateFile,
61 |
62 | [Parameter(
63 | ParameterSetName = 'TemplateParameterFile',
64 | Mandatory = $true
65 | )]
66 | [string] $TemplateParameterFile,
67 |
68 | [Parameter(
69 | ParameterSetName = 'TemplateParameterObject',
70 | Mandatory = $true
71 | )]
72 | [hashtable] $TemplateParameterObject,
73 |
74 | [parameter (
75 | ParameterSetName = "__AllParameterSets",
76 | Mandatory = $false
77 | )]
78 | [ValidateSet("Incremental", "Complete")]
79 | [string] $Mode = "Incremental",
80 |
81 | [parameter(
82 | ParameterSetName = "__AllParameterSets"
83 | )]
84 | [switch] $ThrowWhenRemoving
85 | )
86 | DynamicParam {
87 | if ($TemplateFile) {
88 | #create a new ParameterAttribute Object
89 | $OverRideParameter = New-Object System.Management.Automation.ParameterAttribute
90 | $OverRideParameter.Mandatory = $false
91 | #create an attributecollection object for the attribute we just created.
92 | $AttributeCollection = new-object System.Collections.ObjectModel.Collection[System.Attribute]
93 | $AttributeCollection.Add($OverRideParameter)
94 | $paramDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
95 | $Parameters = (Get-Content $TemplateFile | ConvertFrom-Json).parameters
96 | $ParameterValues = $parameters | Get-Member -MemberType NoteProperty
97 | ForEach ($Param in $ParameterValues) {
98 | $Name = $Param.Name
99 | $type = ($Parameters.$Name).type
100 | #add our paramater specifying the attribute collection
101 | $ExtraParam = New-Object System.Management.Automation.RuntimeDefinedParameter($Param.Name, ($Type -as [type]), $attributeCollection)
102 |
103 | #expose the name of our parameter
104 |
105 | $paramDictionary.Add($Param.Name, $ExtraParam)
106 | }
107 | return $paramDictionary
108 | }
109 | }
110 | process {
111 |
112 | #set variables
113 | $Parameters = @{
114 | ResourceGroupName = $ResourceGroupName
115 | TemplateFile = $TemplateFile
116 | Mode = $Mode
117 | }
118 |
119 | if (-not[string]::IsNullOrEmpty($TemplateParameterFile) ) {
120 | $Parameters.Add("TemplateParameterFile", $TemplateParameterFile)
121 | }
122 | if (-not[string]::IsNullOrEmpty($TemplateParameterObject) ) {
123 | $Parameters.Add("TemplateParameterObject", $TemplateParameterObject)
124 | }
125 | $CustomParameters = (Get-Content $TemplateFile | ConvertFrom-Json).parameters
126 | $CustomParameterValues = $Customparameters | Get-Member -MemberType NoteProperty
127 | foreach ($param in $CustomParameterValues) {
128 | $paramname = $param.Name
129 | if (-not[string]::IsNullOrEmpty($PSBoundParameters.$paramname)) {
130 | $Key = $paramname
131 | $Value = $PSBoundParameters.$paramname
132 | $Parameters.Add($Key, $Value)
133 | }
134 | }
135 |
136 | #Get the AzureModule that's being used
137 | $Module = Test-ARMAzureModule
138 |
139 | $Result = Get-ARMResource @Parameters
140 |
141 | if ([string]::IsNullOrEmpty($Result.Mode)) {
142 | Throw "Something is wrong with the output, no resources found. Please check your deployment with Get-ARMdeploymentErrorMessage"
143 | }
144 | #tell the user if de mode is complete or incremental
145 | Write-Output "Mode for deployment is $($Result.Mode) `n"
146 |
147 | $ValidatedResources = $Result.ValidatedResources
148 | $NewResources = [System.Collections.ArrayList]@()
149 | $ExistingResources = [System.Collections.ArrayList]@()
150 | $DeletedResources = [System.Collections.ArrayList]@()
151 | $OverwrittenResources = [System.Collections.ArrayList]@()
152 | $DifferentResourcegroup = [System.Collections.ArrayList]@()
153 | if ($Module -eq "Az") {
154 | $CheckRGResources = Get-AzResource -ResourceGroupName $ResourceGroupName
155 | }
156 | elseif ($Module -eq "AzureRM") {
157 | $CheckRGResources = Get-AzureRmResource -ResourceGroupName $ResourceGroupName
158 | }
159 | else {
160 | Throw "Something went wrong, No AzureRM of AZ module found"
161 | }
162 | foreach ($CheckRGResource in $CheckRGResources) {
163 | if ($ValidatedResources.Id -notcontains $CheckRGResource.ResourceId -and $Mode -eq "Complete") {
164 | Write-Verbose "Resource $($Resource.name) exists in the resourcegroup and mode is set to Complete"
165 | Write-Verbose "RESOURCE WILL BE DELETED!"
166 | $CheckRGResource.PSObject.TypeNames.Insert(0, 'ArmHelper.ExistingResource')
167 | $null = $DeletedResources.Add($CheckRGResource)
168 | }
169 | }
170 |
171 | foreach ($Resource in $ValidatedResources) {
172 | $Resourceparts = $Resource.Id.Split('/')
173 | if ([string]::IsNullOrEmpty($resource.type)){
174 | $Resource | add-member -MemberType NoteProperty -Name "Name" -Value $Resourceparts[-1]
175 | $resource | Add-Member -MemberType NoteProperty -Name "Type" -Value ($Resourceparts[-3] + "/" + $Resourceparts[-2])
176 | }
177 | #Give a warning that Deployments can give unexpected results
178 | If ($Resource.Type -eq "Microsoft.Resources/deployments") {
179 | Write-Warning "This command does not work for the resourcetype Microsoft.Resources/deployments. Please check $($Resource.Name) manually."
180 | Continue
181 | }
182 | if ($Module -eq "Az") {
183 | $Check = Get-AzResource -Name $Resource.name -ResourceType $Resource.type
184 | }
185 | elseif ($Module -eq "AzureRM") {
186 | $Check = Get-AzureRmResource -Name $Resource.name -ResourceType $Resource.type
187 | }
188 | else {
189 | Throw "Something went wrong, No AzureRM of AZ module found"
190 | }
191 | if ([string]::IsNullOrEmpty($Check)) {
192 | Write-Verbose "Resource $($Resource.name) does not exist, it will be created"
193 | $Resource.PSObject.TypeNames.Insert(0, 'ArmHelper.ExistingResource')
194 | $Resource | Add-Member -MemberType NoteProperty -Name "ResourceGroupName" -Value ($ResourceGroupName) -ErrorAction SilentlyContinue
195 | $null = $NewResources.Add($Resource)
196 | }
197 | else {
198 | if ($Check.ResourceGroupName -eq $ResourceGroupName ) {
199 | if ($Result.Mode -eq "Complete") {
200 | Write-Verbose "Resource $($Resource.name) already exists and mode is set to Complete"
201 | Write-Verbose "RESOURCE WILL BE OVERWRITTEN!"
202 | $Resource.PSObject.TypeNames.Insert(0, 'ArmHelper.ExistingResource')
203 | $Resource | Add-Member -MemberType NoteProperty -Name "ResourceGroupName" -Value ($ResourceGroupName) -ErrorAction SilentlyContinue
204 | $null = $OverwrittenResources.Add($Resource)
205 | }
206 | elseif ($Result.Mode -eq "Incremental") {
207 | Write-Verbose "Resource $($Resource.name) already exists, mode is set to incremental"
208 | Write-Verbose "New properties might be added"
209 | $Resource.PSObject.TypeNames.Insert(0, 'ArmHelper.ExistingResource')
210 | $Resource | Add-Member -MemberType NoteProperty -Name "ResourceGroupName" -Value ($ResourceGroupName) -ErrorAction SilentlyContinue
211 | $null = $ExistingResources.Add($Resource)
212 | }
213 | else {
214 | Write-Error "Resource mode for $($Resource.name) is not clear, please check manually"
215 | }
216 | }
217 | else {
218 | Write-Verbose "$($Resource.name) exists, but in another ResourceGroup. Deployment might fail."
219 | $Resource.PSObject.TypeNames.Insert(0, 'ArmHelper.ExistingResource')
220 | $Resource | Add-Member -MemberType NoteProperty -Name "ResourceGroupName" -Value ($Check.ResourceGroupName) -ErrorAction SilentlyContinue
221 | $null = $DifferentResourcegroup.Add($Resource)
222 | }
223 | }
224 | }
225 | if ($NewResources.count -ne 0) {
226 | Write-Output "The following resources do not exist and will be created:"
227 | $NewResources
228 | Write-Output ""
229 | }
230 |
231 | if ($ExistingResources.count -ne 0) {
232 | Write-Output "The following resources exist. Mode is set to incremental. New properties might be added:"
233 | $ExistingResources
234 | Write-Output ""
235 | }
236 |
237 | if ($OverwrittenResources.Count -ne 0) {
238 | Write-Output "THE FOLLOWING RESOURCES WILL BE OVERWRITTEN! `n Resources exist and mode is complete:"
239 | $OverwrittenResources
240 | Write-Output ""
241 | if ($ThrowWhenRemoving) {
242 | Throw "Resources will be deleted or overwritten."
243 | }
244 | }
245 |
246 | if ($DeletedResources.Count -ne 0) {
247 | Write-Output "THE FOLLOWING RESOURCES WILL BE DELETED! `n Resources exist in the resourcegroup but not in the template, mode is complete:"
248 | $DeletedResources
249 | Write-Output ""
250 | if ($ThrowWhenRemoving) {
251 | Throw "Resources will be deleted or overwritten."
252 | }
253 | }
254 | if ($DifferentResourcegroup.Count -ne 0) {
255 | Write-Output "A resource of the same type and same name exists in other resourcegroup(s). This deployment might fail.`n"
256 | $DifferentResourcegroup
257 | Write-Output ""
258 | }
259 | }
260 | }
261 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | All notable changes to this project will be documented in this file.
4 |
5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7 |
8 | ##[0.6.3] - 2019-10-12
9 |
10 | ### Fixed
11 |
12 | - A change in the Az Module broke most module output. Workarounds have been implemented.
13 | -
14 | ##[0.6.2]
15 |
16 | ### Added
17 |
18 | - Support for overriding parameters #26
19 |
20 | ### Fixed
21 |
22 | - A warning is added to Test-ARMExistingResource as this module can't support Microsoft.Resources/deployments at this time (30)
23 | - Get-ARMDeploymentErrorMessage had issues lately because the output from the Azure Log is not always consistent. It now searches for the right properties in a different way (#32)
24 |
25 | ## [0.5.7] - 2019-08-15
26 |
27 | ### Fixed
28 |
29 | - The check for AzureRm/Az broke for the Azure DevOps pipeline. This is fixed
30 | This also fixes #23 as Az is now the preferred module
31 |
32 | ## [0.5.6] - 2019-07-20
33 |
34 | ### Added
35 |
36 | - Pester testing has been implemented and added to the pipeline #8
37 | - Support for TemplateParameterObject and no parameters at all #5
38 |
39 | ### Fixed
40 |
41 | - The pipeline was broken. It was fixed and improved #18
42 | - SecureString was handled as plain text. The output now shows securestring #19
43 | - minor fixes, like the commenthelp for Test-ARMExistingResource being wrong
44 |
45 | ## [0.3.4] - 2019-06-11
46 |
47 | ### Added
48 |
49 | - PesterTest for Get-ArmdeploymentErrorMessage #8
50 |
51 | ### Fixed
52 |
53 | - Bugfix for Get-ARMDeploymentErrorMessage ending in a timeout #14
54 |
55 | ## [0.3.3] - 2019-05-08
56 |
57 | ### added
58 |
59 | - Support for the AZ module as well as the AzureRM module. #4
60 |
61 | ### Fixed
62 |
63 | - Bugfix to get rid of empty values in Test-ArmDeploymentResource
64 | - Bugfix for empty errormessages in Get-ARMDeploymentResource
65 | - Bugfix Test-ARMDeploymentResource: Network security group output is wrong #3
66 |
67 | ## [0.2.1] - 2019-04-18
68 |
69 | ### added
70 |
71 | - Pipelinesupport for Test-ARMExistingResource
72 | - Changed Get-ArmdeploymentErrormessage switch for pipeline to general throw
73 |
74 | ### Fixed
75 |
76 | - Test-ARMExistingResource showed overwrite-output when mode was nog complete
77 |
78 | ## [0.1.1] - 2019-04-11
79 |
80 | ### Fixed
81 |
82 | - #9 Test-ARMExistingResource should output all resource group changes with complete switch
83 |
84 | ## [0.1.0] - 2019-04-11
85 |
86 | ### Added
87 |
88 | - Test-ARMDEploymentResource now shows "tags" in front of a tag for readability
89 |
90 | ## [0.0.1] - 2019-03-30
91 |
92 | ### Added
93 |
94 | - initial commit
95 |
96 | ---
97 |
98 |
99 | Cheatcheet:
100 |
101 | **Added** for new features.
102 | **Changed** for changes in existing functionality.
103 | **Deprecated** for soon-to-be removed features.
104 | **Removed** for now removed features.
105 | **Fixed** for any bug fixes.
106 | **Security** in case of vulnerabilities.
107 |
--------------------------------------------------------------------------------
/Devazure-pipeline.yml:
--------------------------------------------------------------------------------
1 | name: Development
2 |
3 | trigger:
4 | branches:
5 | include:
6 | - '*' # must quote since "*" is a YAML reserved character; we want a string
7 |
8 | stages:
9 |
10 | - stage: Build
11 | jobs:
12 | - job: BuildWindows
13 | pool:
14 | vmImage: windows-2019
15 | steps:
16 | - template: azure-pipelinestemplate.yml
17 | parameters:
18 | platform: 'Windows'
19 | - template: azure-pipelinesazuretemplate.yml
20 | parameters:
21 | platform: 'Windows'
22 | - job: BuildMacOS
23 | pool:
24 | vmImage: macOS-10.14
25 | steps:
26 | - template: azure-pipelinestemplate.yml
27 | parameters:
28 | platform: 'MacOS'
29 | - job: BuildLinux
30 | pool:
31 | vmImage: ubuntu-16.04
32 | steps:
33 | - template: azure-pipelinestemplate.yml
34 | parameters:
35 | platform: 'Linux'
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Barbara 4bes
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ARMHelper
2 |
3 | ### Development Status
4 |
5 | [](https://dev.azure.com/Ba4bes/ARMHelper/_build/latest?definitionId=17&branchName=Development)
6 | 
7 |
8 | ### Production status
9 |
10 | [](https://dev.azure.com/Ba4bes/ARMHelper/_build/latest?definitionId=8&branchName=master) 
11 |
12 | [](https://img.shields.io/powershellgallery/v/ARMHelper.svg)
13 | [](https://img.shields.io/powershellgallery/dt/ARMHelper.svg)
14 |
15 | ## Introduction
16 |
17 | This module is created to make it easier to work with ARM deployments.
18 |
19 | You are at this point able to:
20 |
21 | - Get detailed errormessages when Azure gives general errors
22 | - Get an overview of all the resources that would get deployed
23 | - Check if resources that you want to deploy already exist and whether they would be overwritten.
24 |
25 | For an introduction, please view
26 |
27 |
28 | For usage in an Azure DevOps pipeline:
29 |
30 | ## Common setup
31 |
32 | ### prerequisites
33 |
34 | - Access to an Azure subscription
35 | - Have the AzureRM or AZ module installed and a connection ready
36 | - Windows PowerShell or PowerShell Core. MacOS and Linux are tested in the pipeline, but could have issues. Please let me know if you run in to one
37 |
38 | ### Installation
39 |
40 | Install ARMHelper from the [PowerShell Gallery](https://powershellgallery.com):
41 |
42 | ```powershell
43 | Install-Module -Name ARMHelper -Scope CurrentUser
44 | Import-Module ARMHelper
45 | ```
46 |
47 | ## Examples
48 |
49 | ### Get a detailed output for a general error
50 |
51 | ```cmd
52 | C:\> Get-ARMDeploymentErrorMessage -ResourceGroupName ARMTest -TemplateFile .\azuredeploy.json -TemplateParameterFile .\azuredeploy.parameters.json
53 |
54 | the output is a generic error message. The log is searched for a more clear errormessage
55 | General Error. Find info below:
56 | ErrorCode: AccountNameInvalid
57 | Errormessage: arm-aqkc32cvb2qmmw is not a valid storage account name. Storage account name must be between 3 and 24 characters in length and use numbers and lower-case letters only.
58 | ```
59 |
60 | ### Get an overview of the new resources that would be deployed
61 |
62 | ```cmd
63 | C:\> Test-ARMDeploymentResource -ResourceGroupName ARMTest -TemplateFile .\azuredeploy.json -TemplateParameterFile .\azuredeploy.parameters.json
64 |
65 | Mode for deployment is Incremental
66 | The following Resources will be deployed:
67 |
68 |
69 | Resource: storageAccounts
70 |
71 | Name : armaqkc32cvb2qmmw
72 | Type : Microsoft.Storage/storageAccounts
73 | ID : /subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/armtesting/providers/Microsoft.Storage/storageAccounts/armaqkc32cvb2qmmw
74 | Location : westeurope
75 | accountType : Standard_LRS
76 | apiVersion : 2015-06-15
77 | ARMcreated : True
78 | displayName : armaqkc32cvb2qmmw
79 | ```
80 |
81 | ### Test if a Resource exists already
82 |
83 | ```cmd
84 | C:\> Test-ARMExistingResource -ResourceGroupName armtesting -TemplateFile .\azuredeploy.json -TemplateParameterFile .\azuredeploy.parameters.json
85 |
86 | Mode for deployment is Incremental
87 | The following resources do not exist and will be created
88 | armaqkc32cvb2qmmw
89 |
90 | C:\> Test-ARMExistingResource -ResourceGroupName armtesting -TemplateFile .\azuredeploy.json -TemplateParameterFile .\azuredeploy.parameters.json
91 |
92 | Mode for deployment is Incremental
93 | The following resources exist. Mode is set to incremental. New properties might be added
94 | armaqkc32cvb2qmmw
95 |
96 | ```
97 |
98 | See Docs-folder for more documentation. (Generated with PlatyPS )
99 |
100 | ## Change Log
101 |
102 | View Change log [here](CHANGELOG.md)
103 |
104 | ## To Contribute
105 |
106 | Any ideas or contributions are welcome!
107 | Please add an issue with your suggestions.
108 |
109 | ## Known Issues
110 |
111 | View known issues [here](https://github.com/Ba4bes/ARMHelper/issues)
112 |
--------------------------------------------------------------------------------
/Tests/AzureTesting/Get-ARMDEploymentErrorMessage.tests.ps1:
--------------------------------------------------------------------------------
1 | $projectRoot = Resolve-Path "$PSScriptRoot\..\.."
2 | $moduleRoot = Split-Path (Resolve-Path "$projectRoot\*\*.psm1")
3 | $moduleName = Split-Path $moduleRoot -Leaf
4 | if (Get-Module ARMHelper) {
5 | Remove-Module -Name ArmHelper -Force
6 | }
7 |
8 |
9 | Import-Module (Join-Path $moduleRoot "$moduleName.psd1") -Force
10 |
11 | Describe 'Check Get-ARMDEploymentErrorMessage with Azure' -Tag @("Az") {
12 | InModuleScope ARMHelper {
13 | Context 'Basic functionality' {
14 | It "When a deployment is correct, output is deployment is correct" {
15 | $Parameters = @{
16 | resourcegroupname = "ArmHelper"
17 | templatefile = "$PSScriptRoot\StorageAccountFixed\azuredeploy.json"
18 | templateparameterfile = "$PSScriptRoot\StorageAccountFixed\azuredeploy.parameters.json"
19 | }
20 | $Result = Get-ARMDeploymentErrorMessage @Parameters
21 | $Result | Should -Be "deployment is correct"
22 | }
23 |
24 | It "Works with a parameterFile" {
25 | $Parameters = @{
26 | resourcegroupname = "ArmHelper"
27 | templatefile = "$PSScriptRoot\StorageAccountFixed\azuredeploy.json"
28 | templateparameterfile = "$PSScriptRoot\StorageAccountFixed\azuredeploy.parameters.json"
29 | }
30 | $Result = Get-ARMDeploymentErrorMessage @Parameters
31 | $Result | Should -Be "deployment is correct"
32 | }
33 | It "works with a parameter object" {
34 | $Parameterobject = @{
35 | storageAccountPrefix = "armsta"
36 | storageAccountType = "Standard_LRS"
37 | }
38 | $Parameters = @{
39 | resourcegroupname = "ArmHelper"
40 | templatefile = "$PSScriptRoot\StorageAccountFixed\azuredeploy.json"
41 | templateparameterobject = $Parameterobject
42 | }
43 | $Result = Get-ARMDeploymentErrorMessage @Parameters
44 | $Result | Should -Be "deployment is correct"
45 | }
46 | It "works with added Parameters" {
47 | $Parameters = @{
48 | resourcegroupname = "ArmHelper"
49 | templatefile = "$PSScriptRoot\StorageAccountFixed\azuredeploy.json"
50 | storageAccountPrefix = "armsta"
51 | storageAccountType = "Standard_LRS"
52 | }
53 | $Result = Get-ARMDeploymentErrorMessage @Parameters
54 | $Result | Should -Be "deployment is correct"
55 | }
56 |
57 | It "When deployment has a regular error, it is given" {
58 | $Parameters = @{
59 | resourcegroupname = "ArmHelper"
60 | templatefile = "$PSScriptRoot\StorageAccountBroken\azuredeploy.json"
61 | templateparameterfile = "$PSScriptRoot\StorageAccountBroken\azuredeploy.parameters.json"
62 | }
63 | $Test = Get-ARMDeploymentErrorMessage @Parameters
64 | $Test[0] | Should -Be "Error, Find info below:"
65 | $Test[1] | Should -Be "Deployment template validation failed: 'The template resource '[variables('storageAccountName')]' at line '32' and column '9' is not valid. The type property is invalid. Please see https://aka.ms/arm-template/#resources for usage details.'."
66 | }
67 |
68 | It "When deployment has a general error, the right results are given" {
69 | $Parameters = @{
70 | resourcegroupname = "ArmHelper"
71 | templatefile = "$PSScriptRoot\StorageAccountGE\azuredeploy.json"
72 | templateparameterfile = "$PSScriptRoot\StorageAccountGE\azuredeploy.parameters.json"
73 | }
74 | $Result = Get-ARMDeploymentErrorMessage @Parameters
75 | $Result[0] | Should -Be "the output is a generic error message. The log is searched for a more clear errormessage"
76 | $Result[-3] | Should -Be "General Error. Find info below:"
77 | $Result[-2] | Should -Be "ErrorCode: AccountNameInvalid"
78 | $Result[-1] | Should -BeLike "* is not a valid storage account name. Storage account name must be between 3 and 24 characters in length and use numbers and lower-case letters only."
79 | }
80 |
81 | Start-Sleep 5
82 | It "Throws when TrowonError is used" {
83 | $Parameters = @{
84 | resourcegroupname = "ArmHelper"
85 | templatefile = "$PSScriptRoot\StorageAccountBroken\azuredeploy.json"
86 | templateparameterfile = "$PSScriptRoot\StorageAccountBroken\azuredeploy.parameters.json"
87 | }
88 | { Get-ARMDeploymentErrorMessage @Parameters -ThrowOnError } | Should -Throw "Deployment is incorrect"
89 | }
90 | It "When no errormessage is found in azurelog, script throws" {
91 | Mock Start-Sleep { $null }
92 | function Get-AzureRMLog([String]$Name, [Object]$Value, [Switch]$Clobber) { }
93 | function Get-AzLog([String]$Name, [Object]$Value, [Switch]$Clobber) { }
94 | Mock Get-AzureRMLog { $null }
95 | Mock Get-AzLog { $null }
96 | $Parameters = @{
97 | resourcegroupname = "ArmHelper"
98 | templatefile = "$PSScriptRoot\StorageAccountGE\azuredeploy.json"
99 | templateparameterfile = "$PSScriptRoot\StorageAccountGE\azuredeploy.parameters.json"
100 | }
101 | { Get-ARMDeploymentErrorMessage @Parameters } | Should -Throw "Can't get Azure Log Entry. Please check the log manually in the portal."
102 | }
103 |
104 | }
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/Tests/AzureTesting/StorageAccountBroken/azuredeploy.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
3 | "contentVersion": "1.0.0.0",
4 | "parameters": {
5 | "storageAccountPrefix": {
6 | "type": "string",
7 | "maxLength": 11,
8 | "defaultValue": "armsta",
9 | "metadata": {
10 | "description": "Prefix the storageaccount"
11 | }
12 | },
13 | "storageAccountType": {
14 | "type": "string",
15 | "defaultValue": "Standard_LRS",
16 | "allowedValues": [
17 | "Standard_LRS",
18 | "Premium_LRS",
19 | "Standard_RAGRS"
20 | ],
21 | "metadata": {
22 | "description": "The type for the storage account"
23 | }
24 | }
25 | },
26 | "variables": {
27 | "storageAccountName": "[toLower(concat(parameters('storageAccountPrefix'), uniqueString(resourceGroup().id)))]"
28 | },
29 | "resources": [
30 | {
31 | "type": "Microsoft.",
32 | "apiVersion": "2015-06-15",
33 | "name": "[variables('storageAccountName')]",
34 | "location": "[resourceGroup().location]",
35 | "tags": {
36 | "displayName": "[variables('storageAccountName')]",
37 | "ARMcreated": "True"
38 | },
39 | "properties": {
40 | "accountType": "[parameters('storageAccountType')]"
41 | }
42 | }
43 | ],
44 | "outputs": {
45 | "storageAccount": {
46 | "type": "string",
47 | "value": "[variables('storageAccountName')]"
48 | }
49 |
50 | }
51 | }
--------------------------------------------------------------------------------
/Tests/AzureTesting/StorageAccountBroken/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 | "storageAccountPrefix": {
6 | "value": "armsta"
7 | },
8 | "storageAccountType": {
9 | "value": "Standard_LRS"
10 | }
11 | }
12 | }
--------------------------------------------------------------------------------
/Tests/AzureTesting/StorageAccountFixed/azuredeploy.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
3 | "contentVersion": "1.0.0.0",
4 | "parameters": {
5 | "storageAccountPrefix": {
6 | "type": "string",
7 | "maxLength": 11,
8 | "defaultValue": "armsta",
9 | "metadata": {
10 | "description": "Prefix the storageaccount"
11 | }
12 | },
13 | "storageAccountType": {
14 | "type": "string",
15 | "defaultValue": "Standard_LRS",
16 | "allowedValues": [
17 | "Standard_LRS",
18 | "Premium_LRS",
19 | "Standard_RAGRS"
20 | ],
21 | "metadata": {
22 | "description": "The type for the storage account"
23 | }
24 | }
25 | },
26 | "variables": {
27 | "storageAccountName": "[toLower(concat(parameters('storageAccountPrefix'), uniqueString(resourceGroup().id)))]"
28 | },
29 | "resources": [
30 | {
31 | "type": "Microsoft.Storage/storageAccounts",
32 | "apiVersion": "2015-06-15",
33 | "name": "[variables('storageAccountName')]",
34 | "location": "[resourceGroup().location]",
35 | "tags": {
36 | "displayName": "[variables('storageAccountName')]",
37 | "ARMcreated": "True"
38 | },
39 | "properties": {
40 | "accountType": "[parameters('storageAccountType')]"
41 | }
42 | }
43 | ],
44 | "outputs": {
45 | "storageAccount": {
46 | "type": "string",
47 | "value": "[variables('storageAccountName')]"
48 | }
49 |
50 | }
51 | }
--------------------------------------------------------------------------------
/Tests/AzureTesting/StorageAccountFixed/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 | "storageAccountPrefix": {
6 | "value": "armsta"
7 | },
8 | "storageAccountType": {
9 | "value": "Standard_LRS"
10 | }
11 | }
12 | }
--------------------------------------------------------------------------------
/Tests/AzureTesting/StorageAccountGE/azuredeploy.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
3 | "contentVersion": "1.0.0.0",
4 | "parameters": {
5 | "storageAccountPrefix": {
6 | "type": "string",
7 | "maxLength": 11,
8 | "defaultValue": "armsta",
9 | "metadata": {
10 | "description": "Prefix the storageaccount"
11 | }
12 | },
13 | "storageAccountType": {
14 | "type": "string",
15 | "defaultValue": "Standard_LRS",
16 | "allowedValues": [
17 | "Standard_LRS",
18 | "Premium_LRS",
19 | "Standard_RAGRS"
20 | ],
21 | "metadata": {
22 | "description": "The type for the storage account"
23 | }
24 | }
25 | },
26 | "variables": {
27 | "storageAccountName": "[toLower(concat(parameters('storageAccountPrefix'), uniqueString(resourceGroup().id)))]"
28 | },
29 | "resources": [
30 | {
31 | "type": "Microsoft.Storage/storageAccounts",
32 | "apiVersion": "2015-06-15",
33 | "name": "[variables('storageAccountName')]",
34 | "location": "[resourceGroup().location]",
35 | "tags": {
36 | "displayName": "[variables('storageAccountName')]",
37 | "ARMcreated": "True"
38 | },
39 | "properties": {
40 | "accountType": "[parameters('storageAccountType')]"
41 | }
42 | }
43 | ],
44 | "outputs": {
45 | "storageAccount": {
46 | "type": "string",
47 | "value": "[variables('storageAccountName')]"
48 | }
49 |
50 | }
51 | }
--------------------------------------------------------------------------------
/Tests/AzureTesting/StorageAccountGE/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 | "storageAccountPrefix": {
6 | "value": "sa!"
7 | },
8 | "storageAccountType": {
9 | "value": "Standard_LRS"
10 | }
11 | }
12 | }
--------------------------------------------------------------------------------
/Tests/AzureTesting/Test-ARMDeploymentResource.tests.ps1:
--------------------------------------------------------------------------------
1 | $projectRoot = Resolve-Path "$PSScriptRoot\..\.."
2 | $moduleRoot = Split-Path (Resolve-Path "$projectRoot\*\*.psm1")
3 | $moduleName = Split-Path $moduleRoot -Leaf
4 | if (Get-Module ARMHelper) {
5 | Remove-Module -Name ARMHelper -Force
6 | }
7 | Import-Module (Join-Path $moduleRoot "$moduleName.psd1") -Force
8 |
9 |
10 | Describe 'Check Test-ARMDeploymentResource with Azure' -Tag @("Az") {
11 | InModuleScope ARMHelper {
12 | $Parameters = @{
13 | resourcegroupname = "ArmHelper"
14 | templatefile = "$PSScriptRoot\VirtualMachine\azuredeploy.json"
15 | templateparameterfile = "$PSScriptRoot\VirtualMachine\azuredeploy.parameters.json"
16 | }
17 | Context 'Basic functionallity' {
18 |
19 | it "Gives a warning for using Az-module"{
20 | $Result = (Test-ARMDeploymentResource @Parameters 3>&1)
21 | if ($Module -eq "Az"){
22 | $Result.Message[0] | Should -BeLike "The AZ-module is used. This limits the results of this cmdlet*"
23 | }
24 | if ($Module -eq "AzureRM"){
25 | $Result.Message[0] | Should -Not -BeLike "The AZ-module is used. This limits the results of this cmdlet*"
26 | }
27 | }
28 | It "Works with a parameterFile" {
29 | { Test-ARMDeploymentResource @Parameters -WarningAction SilentlyContinue } | Should -Not -Throw
30 | }
31 | It "works with a parameter object" {
32 | $Parameterobject = @{
33 | adminUsername = "Test"
34 | adminPassword = "Welkom123"
35 | dnsLabelPrefix = "eandomrlkajelnadada"
36 | }
37 | $Parameters = @{
38 | resourcegroupname = "ArmHelper"
39 | templatefile = "$PSScriptRoot\VirtualMachine\azuredeploy.json"
40 | templateparameterobject = $Parameterobject
41 | }
42 | { Test-ARMDeploymentResource @Parameters -WarningAction SilentlyContinue } | Should -Not -Throw
43 |
44 | }
45 | It "works with added Parameters" {
46 | $Parameters = @{
47 | resourcegroupname = "ArmHelper"
48 | templatefile = "$PSScriptRoot\VirtualMachine\azuredeploy.json"
49 | adminPassword = ( "VeryNicePassword" | ConvertTo-SecureString -AsPlainText -force )
50 | adminUsername = "Test"
51 | dnsLabelPrefix = "eandomrlkajelnadada"
52 | }
53 | { Test-ARMDeploymentResource @Parameters -WarningAction SilentlyContinue } | Should -Not -Throw
54 | }
55 | It "When a deployment is correct, script doesn't throw" {
56 | { Test-ARMDeploymentResource @Parameters -WarningAction SilentlyContinue } | Should -Not -Throw
57 | }
58 | It "Shows standard properties for resources that would be in the deployment" {
59 | $Result = Test-ARMDeploymentResource @Parameters -WarningAction SilentlyContinue
60 | if ($Module -eq "AzureRM"){
61 | $Result[0].Resource | Should -be "StorageAccounts"
62 | $Result[0].Name | Should -be "mstaqkc32c2qmmw"
63 | $Result[0].Type | Should -be "Microsoft.Storage/storageAccounts"
64 | $Result[0].Location | Should -be "westeurope"
65 | $Result[0].mode | Should -be "Incremental"
66 | $Result[0].ID | Should -BeLike "*/resourceGroups/ArmHelper/providers/Microsoft.Storage/storageAccounts/mstaqkc32c2qmmw"
67 | $Result[0].kind | Should -be "Storage"
68 | $Result[0].sku.name | Should -BeNullOrEmpty
69 | }
70 | if ($Module -eq "Az"){
71 | $Result[0].Resource | Should -be "StorageAccounts"
72 | $Result[0].Name | Should -be "mstaqkc32c2qmmw"
73 | $Result[0].Type | Should -be "Microsoft.Storage/storageAccounts"
74 | $Result[0].ID | Should -BeLike "*/resourceGroups/ArmHelper/providers/Microsoft.Storage/storageAccounts/mstaqkc32c2qmmw"
75 |
76 | }
77 | }
78 | if ($Module -eq "AzureRM"){
79 | It "Shows all properties when using Select-Object *" {
80 | if ($Module -eq "AzureRM"){
81 | $Result = Test-ARMDeploymentResource @Parameters -WarningAction SilentlyContinue | Select-Object *
82 | $result[0].'.sku.name' | Should -be "Standard_LRS"
83 | $Result[0].apiVersion | Should -be "2018-11-01"
84 |
85 | }
86 | it "When a parameter is a securestring, it is shown as a securestring in the output" {
87 |
88 | $Result = Test-ARMDeploymentResource -WarningAction SilentlyContinue @Parameters
89 | $Result[-1].adminPassword | Should -Be "System.Security.SecureString"
90 | }
91 | }
92 | }
93 | it "Should throw when no result is found" {
94 | $Parameters = @{
95 | resourcegroupname = "ArmHelper"
96 | templatefile = "$PSScriptRoot\StorageAccountBroken\azuredeploy.json"
97 | templateparameterfile = "$PSScriptRoot\StorageAccountBroken\azuredeploy.parameters.json"
98 | }
99 | { Test-ARMDeploymentResource @Parameters -WarningAction SilentlyContinue } | Should throw "Something is wrong with the output, no resources found. Please check your deployment with Get-ARMdeploymentErrorMessage"
100 | }
101 | }
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/Tests/AzureTesting/VirtualMachine/azuredeploy.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
3 | "contentVersion": "1.0.0.0",
4 | "parameters": {
5 | "adminUsername": {
6 | "type": "string",
7 | "metadata": {
8 | "description": "Username for the Virtual Machine."
9 | }
10 | },
11 | "adminPassword": {
12 | "type": "securestring",
13 | "metadata": {
14 | "description": "Password for the Virtual Machine."
15 | }
16 | },
17 | "dnsLabelPrefix": {
18 | "type": "string",
19 | "metadata": {
20 | "description": "Unique DNS Name for the Public IP used to access the Virtual Machine."
21 | }
22 | },
23 | "windowsOSVersion": {
24 | "type": "string",
25 | "defaultValue": "2016-Datacenter",
26 | "allowedValues": [
27 | "2008-R2-SP1",
28 | "2012-Datacenter",
29 | "2012-R2-Datacenter",
30 | "2016-Nano-Server",
31 | "2016-Datacenter-with-Containers",
32 | "2016-Datacenter",
33 | "2019-Datacenter"
34 | ],
35 | "metadata": {
36 | "description": "The Windows version for the VM. This will pick a fully patched image of this given Windows version."
37 | }
38 | },
39 | "location": {
40 | "type": "string",
41 | "defaultValue": "[resourceGroup().location]",
42 | "metadata": {
43 | "description": "Location for all resources."
44 | }
45 | }
46 | },
47 | "variables": {
48 | "storageAccountName": "mstaqkc32c2qmmw",
49 | "nicName": "myVMNic",
50 | "addressPrefix": "10.0.0.0/16",
51 | "subnetName": "Subnet",
52 | "subnetPrefix": "10.0.0.0/24",
53 | "publicIPAddressName": "myPublicIP",
54 | "vmName": "SimpleWinVM",
55 | "virtualNetworkName": "MyVNET",
56 | "subnetRef": "[resourceId('Microsoft.Network/virtualNetworks/subnets', variables('virtualNetworkName'), variables('subnetName'))]"
57 | },
58 | "resources": [
59 | {
60 | "type": "Microsoft.Storage/storageAccounts",
61 | "apiVersion": "2018-11-01",
62 | "name": "[variables('storageAccountName')]",
63 | "location": "[parameters('location')]",
64 | "sku": {
65 | "name": "Standard_LRS"
66 | },
67 | "kind": "Storage",
68 | "properties": {}
69 | },
70 | {
71 | "type": "Microsoft.Network/publicIPAddresses",
72 | "apiVersion": "2018-11-01",
73 | "name": "[variables('publicIPAddressName')]",
74 | "location": "[parameters('location')]",
75 | "properties": {
76 | "publicIPAllocationMethod": "Dynamic",
77 | "dnsSettings": {
78 | "domainNameLabel": "[parameters('dnsLabelPrefix')]"
79 | }
80 | }
81 | },
82 | {
83 | "type": "Microsoft.Network/virtualNetworks",
84 | "apiVersion": "2018-11-01",
85 | "name": "[variables('virtualNetworkName')]",
86 | "location": "[parameters('location')]",
87 | "properties": {
88 | "addressSpace": {
89 | "addressPrefixes": [
90 | "[variables('addressPrefix')]"
91 | ]
92 | },
93 | "subnets": [
94 | {
95 | "name": "[variables('subnetName')]",
96 | "properties": {
97 | "addressPrefix": "[variables('subnetPrefix')]"
98 | }
99 | }
100 | ]
101 | }
102 | },
103 | {
104 | "type": "Microsoft.Network/networkInterfaces",
105 | "apiVersion": "2018-11-01",
106 | "name": "[variables('nicName')]",
107 | "location": "[parameters('location')]",
108 | "dependsOn": [
109 | "[resourceId('Microsoft.Network/publicIPAddresses/', variables('publicIPAddressName'))]",
110 | "[resourceId('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]"
111 | ],
112 | "properties": {
113 | "ipConfigurations": [
114 | {
115 | "name": "ipconfig1",
116 | "properties": {
117 | "privateIPAllocationMethod": "Dynamic",
118 | "publicIPAddress": {
119 | "id": "[resourceId('Microsoft.Network/publicIPAddresses',variables('publicIPAddressName'))]"
120 | },
121 | "subnet": {
122 | "id": "[variables('subnetRef')]"
123 | }
124 | }
125 | }
126 | ]
127 | }
128 | },
129 | {
130 | "type": "Microsoft.Compute/virtualMachines",
131 | "apiVersion": "2018-10-01",
132 | "name": "[variables('vmName')]",
133 | "location": "[parameters('location')]",
134 | "dependsOn": [
135 | "[resourceId('Microsoft.Storage/storageAccounts/', variables('storageAccountName'))]",
136 | "[resourceId('Microsoft.Network/networkInterfaces/', variables('nicName'))]"
137 | ],
138 | "properties": {
139 | "hardwareProfile": {
140 | "vmSize": "Standard_A2"
141 | },
142 | "osProfile": {
143 | "computerName": "[variables('vmName')]",
144 | "adminUsername": "[parameters('adminUsername')]",
145 | "adminPassword": "[parameters('adminPassword')]"
146 | },
147 | "storageProfile": {
148 | "imageReference": {
149 | "publisher": "MicrosoftWindowsServer",
150 | "offer": "WindowsServer",
151 | "sku": "[parameters('windowsOSVersion')]",
152 | "version": "latest"
153 | },
154 | "osDisk": {
155 | "createOption": "FromImage"
156 | },
157 | "dataDisks": [
158 | {
159 | "diskSizeGB": 1023,
160 | "lun": 0,
161 | "createOption": "Empty"
162 | }
163 | ]
164 | },
165 | "networkProfile": {
166 | "networkInterfaces": [
167 | {
168 | "id": "[resourceId('Microsoft.Network/networkInterfaces',variables('nicName'))]"
169 | }
170 | ]
171 | },
172 | "diagnosticsProfile": {
173 | "bootDiagnostics": {
174 | "enabled": true,
175 | "storageUri": "[reference(resourceId('Microsoft.Storage/storageAccounts/', variables('storageAccountName'))).primaryEndpoints.blob]"
176 | }
177 | }
178 | }
179 | }
180 | ],
181 | "outputs": {
182 | "hostname": {
183 | "type": "string",
184 | "value": "[reference(variables('publicIPAddressName')).dnsSettings.fqdn]"
185 | }
186 | }
187 | }
--------------------------------------------------------------------------------
/Tests/AzureTesting/VirtualMachine/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 | "adminUsername": {
6 | "value": "Test"
7 | },
8 | "adminPassword": {
9 | "value": "Welkom123"
10 | },
11 | "dnsLabelPrefix": {
12 | "value": "eandomrlkajelnadada"
13 | },
14 | "windowsOSVersion": {
15 | "value": "2016-Datacenter"
16 | }
17 | }
18 | }
--------------------------------------------------------------------------------
/Tests/AzureTesting/pipelinetest.ps1:
--------------------------------------------------------------------------------
1 | $projectRoot = Resolve-Path "$PSScriptRoot\..\.."
2 | $moduleRoot = Split-Path (Resolve-Path "$projectRoot\*\*.psm1")
3 | $moduleName = Split-Path $moduleRoot -Leaf
4 | if (Get-Module ARMHelper) {
5 | Remove-Module -Name ArmHelper -Force
6 | }
7 |
8 | Import-Module (Join-Path $moduleRoot "$moduleName.psd1") -Force
9 |
10 | Describe 'Check Get-ARMDEploymentErrorMessage with Azure' -Tag @("Az") {
11 | InModuleScope ARMHelper {
12 | Context 'Basic functionality' {
13 | It "When a deployment is correct, output is deployment is correct" {
14 | $Parameters = @{
15 | resourcegroupname = "ArmHelper"
16 | templatefile = "$PSScriptRoot\StorageAccountFixed\azuredeploy.json"
17 | templateparameterfile = "$PSScriptRoot\StorageAccountFixed\azuredeploy.parameters.json"
18 | }
19 | $Result = Get-ARMDeploymentErrorMessage @Parameters
20 | $Result | Should -Be "deployment is correct"
21 | }
22 |
23 | It "Works with a parameterFile" {
24 | $Parameters = @{
25 | resourcegroupname = "ArmHelper"
26 | templatefile = "$PSScriptRoot\StorageAccountFixed\azuredeploy.json"
27 | templateparameterfile = "$PSScriptRoot\StorageAccountFixed\azuredeploy.parameters.json"
28 | }
29 | $Result = Get-ARMDeploymentErrorMessage @Parameters
30 | $Result | Should -Be "deployment is correct"
31 | }
32 | It "works with a parameter object" {
33 | $Parameterobject = @{
34 | storageAccountPrefix = "armsta"
35 | storageAccountType = "Standard_LRS"
36 | }
37 | $Parameters = @{
38 | resourcegroupname = "ArmHelper"
39 | templatefile = "$PSScriptRoot\StorageAccountFixed\azuredeploy.json"
40 | templateparameterobject = $Parameterobject
41 | }
42 | $Result = Get-ARMDeploymentErrorMessage @Parameters
43 | $Result | Should -Be "deployment is correct"
44 | }
45 | It "works with added Parameters" {
46 | $Parameters = @{
47 | resourcegroupname = "ArmHelper"
48 | templatefile = "$PSScriptRoot\StorageAccountFixed\azuredeploy.json"
49 | storageAccountPrefix = "armsta"
50 | storageAccountType = "Standard_LRS"
51 | }
52 | $Result = Get-ARMDeploymentErrorMessage @Parameters
53 | $Result | Should -Be "deployment is correct"
54 | }
55 |
56 | It "When deployment has a regular error, it is given" {
57 | $Parameters = @{
58 | resourcegroupname = "ArmHelper"
59 | templatefile = "$PSScriptRoot\StorageAccountBroken\azuredeploy.json"
60 | templateparameterfile = "$PSScriptRoot\StorageAccountBroken\azuredeploy.parameters.json"
61 | }
62 | $Test = Get-ARMDeploymentErrorMessage @Parameters
63 | $Test[0] | Should -Be "Error, Find info below:"
64 | $Test[1] | Should -Be "Deployment template validation failed: 'The template resource '[variables('storageAccountName')]' at line '32' and column '9' is not valid. The type property is invalid. Please see https://aka.ms/arm-template/#resources for usage details.'."
65 | }
66 | # It "When deployment has a general error, the right results are given" {
67 | # $Parameters = @{
68 | # resourcegroupname = "ArmHelper"
69 | # templatefile = "$PSScriptRoot\StorageAccountGE\azuredeploy.json"
70 | # templateparameterfile = "$PSScriptRoot\StorageAccountGE\azuredeploy.parameters.json"
71 | # }
72 | # $Result = Get-ARMDeploymentErrorMessage @Parameters
73 | # $Result[0] | Should -Be "the output is a generic error message. The log is searched for a more clear errormessage"
74 | # $Result[-3] | Should -Be "General Error. Find info below:"
75 | # $Result[-2] | Should -Be "ErrorCode: AccountNameInvalid"
76 | # $Result[-1] | Should -BeLike "* is not a valid storage account name. Storage account name must be between 3 and 24 characters in length and use numbers and lower-case letters only."
77 | # }
78 |
79 | Start-Sleep 5
80 | It "Throws when TrowonError is used" {
81 | $Parameters = @{
82 | resourcegroupname = "ArmHelper"
83 | templatefile = "$PSScriptRoot\StorageAccountBroken\azuredeploy.json"
84 | templateparameterfile = "$PSScriptRoot\StorageAccountBroken\azuredeploy.parameters.json"
85 | }
86 | { Get-ARMDeploymentErrorMessage @Parameters -ThrowOnError } | Should -Throw "Deployment is incorrect"
87 | }
88 | # It "When no errormessage is found in azurelog, script throws" {
89 | # Mock Start-Sleep { $null }
90 | # function Get-AzureRMLog([String]$Name, [Object]$Value, [Switch]$Clobber) { }
91 | # function Get-AzLog([String]$Name, [Object]$Value, [Switch]$Clobber) { }
92 | # Mock Get-AzureRMLog { $null }
93 | # Mock Get-AzLog { $null }
94 | # $Parameters = @{
95 | # resourcegroupname = "ArmHelper"
96 | # templatefile = "$PSScriptRoot\StorageAccountGE\azuredeploy.json"
97 | # templateparameterfile = "$PSScriptRoot\StorageAccountGE\azuredeploy.parameters.json"
98 | # }
99 | # { Get-ARMDeploymentErrorMessage @Parameters } | Should -Throw "Can't get Azure Log Entry. Please check the log manually in the portal."
100 | # }
101 |
102 | }
103 | }
104 | }
105 |
106 | $projectRoot = Resolve-Path "$PSScriptRoot\..\.."
107 | $moduleRoot = Split-Path (Resolve-Path "$projectRoot\*\*.psm1")
108 | $moduleName = Split-Path $moduleRoot -Leaf
109 | if (Get-Module ARMHelper) {
110 | Remove-Module -Name ARMHelper -Force
111 | }
112 | Import-Module (Join-Path $moduleRoot "$moduleName.psd1") -Force
113 |
114 |
115 | Describe 'Check Test-ARMDeploymentResource with Azure' -Tag @("Az") {
116 | InModuleScope ARMHelper {
117 | $Parameters = @{
118 | resourcegroupname = "ArmHelper"
119 | templatefile = "$PSScriptRoot\VirtualMachine\azuredeploy.json"
120 | templateparameterfile = "$PSScriptRoot\VirtualMachine\azuredeploy.parameters.json"
121 | }
122 | Context 'Basic functionallity' {
123 |
124 | it "Gives a warning for using Az-module"{
125 | $Result = (Test-ARMDeploymentResource @Parameters 3>&1)
126 | if ($Module -eq "Az"){
127 | $Result.Message[0] | Should -BeLike "The AZ-module is used. This limits the results of this cmdlet*"
128 | }
129 | if ($Module -eq "AzureRM"){
130 | $Result.Message[0] | Should -Not -BeLike "The AZ-module is used. This limits the results of this cmdlet*"
131 | }
132 | }
133 | It "Works with a parameterFile" {
134 | { Test-ARMDeploymentResource @Parameters -WarningAction SilentlyContinue } | Should -Not -Throw
135 | }
136 | It "works with a parameter object" {
137 | $Parameterobject = @{
138 | adminUsername = "Test"
139 | adminPassword = "Welkom123"
140 | dnsLabelPrefix = "eandomrlkajelnadada"
141 | }
142 | $Parameters = @{
143 | resourcegroupname = "ArmHelper"
144 | templatefile = "$PSScriptRoot\VirtualMachine\azuredeploy.json"
145 | templateparameterobject = $Parameterobject
146 | }
147 | { Test-ARMDeploymentResource @Parameters -WarningAction SilentlyContinue } | Should -Not -Throw
148 |
149 | }
150 | It "works with added Parameters" {
151 | $Parameters = @{
152 | resourcegroupname = "ArmHelper"
153 | templatefile = "$PSScriptRoot\VirtualMachine\azuredeploy.json"
154 | adminPassword = ( "VeryNicePassword" | ConvertTo-SecureString -AsPlainText -force )
155 | adminUsername = "Test"
156 | dnsLabelPrefix = "eandomrlkajelnadada"
157 | }
158 | { Test-ARMDeploymentResource @Parameters -WarningAction SilentlyContinue } | Should -Not -Throw
159 | }
160 | It "When a deployment is correct, script doesn't throw" {
161 | { Test-ARMDeploymentResource @Parameters -WarningAction SilentlyContinue } | Should -Not -Throw
162 | }
163 | It "Shows standard properties for resources that would be in the deployment" {
164 | $Result = Test-ARMDeploymentResource @Parameters -WarningAction SilentlyContinue
165 | if ($Module -eq "AzureRM"){
166 | $Result[0].Resource | Should -be "StorageAccounts"
167 | $Result[0].Name | Should -be "mstaqkc32c2qmmw"
168 | $Result[0].Type | Should -be "Microsoft.Storage/storageAccounts"
169 | $Result[0].Location | Should -be "westeurope"
170 | $Result[0].mode | Should -be "Incremental"
171 | $Result[0].ID | Should -BeLike "*/resourceGroups/ArmHelper/providers/Microsoft.Storage/storageAccounts/mstaqkc32c2qmmw"
172 | $Result[0].kind | Should -be "Storage"
173 | $Result[0].sku.name | Should -BeNullOrEmpty
174 | }
175 | if ($Module -eq "Az"){
176 | $Result[0].Resource | Should -be "StorageAccounts"
177 | $Result[0].Name | Should -be "mstaqkc32c2qmmw"
178 | $Result[0].Type | Should -be "Microsoft.Storage/storageAccounts"
179 | $Result[0].ID | Should -BeLike "*/resourceGroups/ArmHelper/providers/Microsoft.Storage/storageAccounts/mstaqkc32c2qmmw"
180 |
181 | }
182 | }
183 | if ($Module -eq "AzureRM"){
184 | It "Shows all properties when using Select-Object *" {
185 | if ($Module -eq "AzureRM"){
186 | $Result = Test-ARMDeploymentResource @Parameters -WarningAction SilentlyContinue | Select-Object *
187 | $result[0].'.sku.name' | Should -be "Standard_LRS"
188 | $Result[0].apiVersion | Should -be "2018-11-01"
189 |
190 | }
191 | it "When a parameter is a securestring, it is shown as a securestring in the output" {
192 |
193 | $Result = Test-ARMDeploymentResource -WarningAction SilentlyContinue @Parameters
194 | $Result[-1].adminPassword | Should -Be "System.Security.SecureString"
195 | }
196 | }
197 | }
198 | it "Should throw when no result is found" {
199 | $Parameters = @{
200 | resourcegroupname = "ArmHelper"
201 | templatefile = "$PSScriptRoot\StorageAccountBroken\azuredeploy.json"
202 | templateparameterfile = "$PSScriptRoot\StorageAccountBroken\azuredeploy.parameters.json"
203 | }
204 | { Test-ARMDeploymentResource @Parameters -WarningAction SilentlyContinue } | Should throw "Something is wrong with the output, no resources found. Please check your deployment with Get-ARMdeploymentErrorMessage"
205 | }
206 | }
207 | }
208 | }
--------------------------------------------------------------------------------
/Tests/Basic_Module.tests.ps1:
--------------------------------------------------------------------------------
1 | # Copied from https://powershellexplained.com/2017-01-21-powershell-module-continious-delivery-pipeline/
2 |
3 | $projectRoot = Resolve-Path "$PSScriptRoot\.."
4 | $moduleRoot = Split-Path (Resolve-Path "$projectRoot\*\*.psm1")
5 | $moduleName = Split-Path $moduleRoot -Leaf
6 |
7 |
8 | Describe "General project validation: $moduleName" {
9 |
10 | $scripts = Get-ChildItem $projectRoot -Include *.ps1, *.psm1, *.psd1 -Recurse
11 |
12 | # TestCases are splatted to the script so we need hashtables
13 | $testCase = $scripts | Foreach-Object {
14 | @{
15 | file = $_
16 | }
17 | }
18 |
19 | It "Script should exist" -TestCases $testCase {
20 | param($file)
21 |
22 | $file.fullname | Should -Exist
23 | }
24 |
25 | It "Script can be read without errors" -TestCases $testCase {
26 | param($file)
27 |
28 | { $null = Get-Content -Path $file.fullname -ErrorAction Stop } | Should -Not -Throw
29 | }
30 |
31 | It "Script should be valid powershell" -TestCases $testCase {
32 | param($file)
33 |
34 | $contents = Get-Content -Path $file.fullname -ErrorAction SilentlyContinue
35 | $errors = $null
36 | $null = [System.Management.Automation.PSParser]::Tokenize($contents, [ref]$errors)
37 | $errors.Count | Should -Be 0
38 | }
39 |
40 | It "Module '$moduleName' can import cleanly" {
41 | { Import-Module (Join-Path $moduleRoot "$moduleName.psm1") -force } | Should -Not -Throw
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/Tests/Get-ARMDeploymentErrorMessage.tests.ps1:
--------------------------------------------------------------------------------
1 | $projectRoot = Resolve-Path "$PSScriptRoot\.."
2 | $moduleRoot = Split-Path (Resolve-Path "$projectRoot\*\*.psm1")
3 | $moduleName = Split-Path $moduleRoot -Leaf
4 |
5 | Import-Module (Join-Path $moduleRoot "$moduleName.psd1") -force
6 |
7 |
8 |
9 | Describe 'Check Get-ARMDEploymentErrorMessage' -Tag @("Mock") {
10 | InModuleScope ARMHelper {
11 | $Parameters = @{
12 | resourcegroupname = "Arm"
13 | templatefile = "$PSScriptRoot\MockObjects\azuredeploy.json"
14 | templateparameterfile = ".\azuredeploy.parameters.json"
15 | }
16 | Context 'Basic functionality AzureRM' {
17 | function Test-AzureRMResourceGroupDeployment([String]$Name, [Object]$Value, [Switch]$Clobber) { }
18 |
19 | function Get-AzureRMLog([String]$Name, [Object]$Value, [Switch]$Clobber) { }
20 | Mock Test-ARMAzureModule { "AzureRM" }
21 | It "Works with a parameterFile" {
22 | Mock Test-AzureRmResourceGroupDeployment -parameterfilter { $Parameters } { $null }
23 | $Result = Get-ARMDeploymentErrorMessage @Parameters
24 | $Result | Should -Be "deployment is correct"
25 | }
26 | It "works with a parameter object" {
27 | $Parameterobject = @{
28 | storageAccountPrefix = "armsta"
29 | storageAccountType = "LRS"
30 | }
31 | $Parameters = @{
32 | resourcegroupname = "Arm"
33 | templatefile = "$PSScriptRoot\MockObjects\azuredeploy.json"
34 | templateparameterobject = $Parameterobject
35 | }
36 | Mock Test-AzureRmResourceGroupDeployment -parameterfilter { $Parameters } { $null }
37 | $Result = Get-ARMDeploymentErrorMessage @Parameters
38 | $Result | Should -Be "deployment is correct"
39 | }
40 | It "works with added Parameters" {
41 | $Parameters = @{
42 | resourcegroupname = "Arm"
43 | templatefile = "$PSScriptRoot\MockObjects\azuredeploy.json"
44 | storageAccountPrefix = "armsta"
45 | storageAccountType = "LRS"
46 | }
47 | Mock Test-AzureRmResourceGroupDeployment -parameterfilter { $Parameters } { $null }
48 | $Result = Get-ARMDeploymentErrorMessage @Parameters
49 | $Result | Should -Be "deployment is correct"
50 | }
51 | It "When a deployment is correct, output is deployment is correct" {
52 | Mock Test-AzureRmResourceGroupDeployment -parameterfilter { $Parameters } { $null }
53 | $Result = Get-ARMDeploymentErrorMessage @Parameters
54 | $Result | Should -Be "deployment is correct"
55 | }
56 | It "When deployment has a regular error, it is given" {
57 | Mock Test-AzureRmResourceGroupDeployment -parameterfilter { $Parameters } {
58 | [pscustomobject]@{
59 | Code = 'InvalidTemplateDeployment'
60 | Message = "Deployment template validation failed"
61 | }
62 | }
63 | $Test = Get-ARMDeploymentErrorMessage @Parameters
64 | $Test[0] | Should -Be "Error, Find info below:"
65 | $Test[1] | Should -Be "Deployment template validation failed"
66 | }
67 | It "When deployment has a general error, the right results are given" {
68 | Mock Test-AzureRmResourceGroupDeployment -parameterfilter { $Parameters } {
69 | [pscustomobject]@{
70 | Code = 'InvalidTemplateDeployment'
71 | Message = "The template deployment '12345678-1234-1234-1234-12345678abcd' is not valid according to the validation procedure. The
72 | tracking id is '12345678-1234-1234-1234-12345678abcd'. See inner errors for details. Please see https://aka.ms/arm-deploy
73 | for usage details."
74 | }
75 | }
76 | $Mockobject = (Get-Content $PSScriptRoot\MockObjects\Logoutput.json) | ConvertFrom-Json
77 | Mock Get-AzureRMLog {
78 | [object]$Mockobject
79 | }
80 | Mock Start-Sleep { $null }
81 | $Result = Get-ARMDeploymentErrorMessage @Parameters
82 | $Result[0] | Should -Be "the output is a generic error message. The log is searched for a more clear errormessage"
83 | $Result[-3] | Should -Be "General Error. Find info below:"
84 | $Result[-2] | Should -Be "ErrorCode: AccountNameInvalid"
85 | $Result[-1] | Should -Be "Errormessage: s aqkc32cvb2qmmw is not a valid storage account name. Storage account name must be between 3 and 24 characters in length and use numbers and lower-case letters only."
86 | }
87 | It "When no errormessage is found in azurelog, script throws" {
88 | Mock Test-AzureRmResourceGroupDeployment -parameterfilter { $Parameters } {
89 | [pscustomobject]@{
90 | Code = 'InvalidTemplateDeployment'
91 | Message = "The template deployment '12345678-1234-1234-1234-12345678abcd' is not valid according to the validation procedure. The
92 | tracking id is '12345678-1234-1234-1234-12345678abcd'. See inner errors for details. Please see https://aka.ms/arm-deploy
93 | for usage details."
94 | }
95 | }
96 | Mock Get-AzureRMLog {
97 | $null
98 | }
99 | Mock Start-Sleep { $null }
100 | { Get-ARMDeploymentErrorMessage @Parameters } | Should -Throw "Can't get Azure Log Entry. Please check the log manually in the portal."
101 | }
102 | It "Throws when TrowonError is used" {
103 | Mock Test-AzureRmResourceGroupDeployment -parameterfilter { $Parameters } {
104 | [pscustomobject]@{
105 | Code = 'InvalidTemplateDeployment'
106 | Message = "The template deployment '12345678-1234-1234-1234-12345678abcd' is not valid according to the validation procedure. The
107 | tracking id is '12345678-1234-1234-1234-12345678abcd'. See inner errors for details. Please see https://aka.ms/arm-deploy
108 | for usage details."
109 | }
110 | }
111 | $Mockobject = (Get-Content $PSScriptRoot\MockObjects\Logoutput.json) | ConvertFrom-Json
112 | Mock Get-AzureRMLog {
113 | [object]$Mockobject
114 | }
115 | Mock Start-Sleep { $null }
116 | { Get-ARMDeploymentErrorMessage @Parameters -ThrowOnError } | Should -Throw "Deployment is incorrect"
117 |
118 | }
119 | It "All Mocks are called" {
120 | Assert-MockCalled -CommandName Get-AzureRmLog
121 | Assert-MockCalled -CommandName Test-AzureRmResourceGroupDeployment
122 | }
123 | }
124 | Context 'Basic functionality Az' {
125 | function Test-AzResourceGroupDeployment([String]$Name, [Object]$Value, [Switch]$Clobber) { }
126 | function Get-AzLog([String]$Name, [Object]$Value, [Switch]$Clobber) { }
127 |
128 | Mock Test-ARMAzureModule { "Az" }
129 | It "Works with a parameterFile" {
130 | Mock Test-AzResourceGroupDeployment -parameterfilter { $Parameters } { $null }
131 | $Result = Get-ARMDeploymentErrorMessage @Parameters
132 | $Result | Should -Be "deployment is correct"
133 | }
134 | It "works with a parameter object" {
135 | $Parameterobject = @{
136 | storageAccountPrefix = "armsta"
137 | storageAccountType = "LRS"
138 | }
139 | $Parameters = @{
140 | resourcegroupname = "Arm"
141 | templatefile = "$PSScriptRoot\MockObjects\azuredeploy.json"
142 | templateparameterobject = $Parameterobject
143 | }
144 | Mock Test-AzResourceGroupDeployment -parameterfilter { $Parameters } { $null }
145 | $Result = Get-ARMDeploymentErrorMessage @Parameters
146 | $Result | Should -Be "deployment is correct"
147 | }
148 | It "works with added Parameters" {
149 | $Parameters = @{
150 | resourcegroupname = "Arm"
151 | templatefile = "$PSScriptRoot\MockObjects\azuredeploy.json"
152 | storageAccountPrefix = "armsta"
153 | storageAccountType = "LRS"
154 | }
155 | Mock Test-AzResourceGroupDeployment -parameterfilter { $Parameters } { $null }
156 | $Result = Get-ARMDeploymentErrorMessage @Parameters
157 | $Result | Should -Be "deployment is correct"
158 | }
159 | It "When a deployment is correct, output is deployment is correct" {
160 | Mock Test-AzResourceGroupDeployment -parameterfilter { $Parameters } { $null }
161 | $Result = Get-ARMDeploymentErrorMessage @Parameters
162 | $Result | Should -Be "deployment is correct"
163 | }
164 | It "When deployment has a regular error, it is given" {
165 | Mock Test-AzResourceGroupDeployment -parameterfilter { $Parameters } {
166 | [pscustomobject]@{
167 | Code = 'InvalidTemplateDeployment'
168 | Message = "Deployment template validation failed"
169 | }
170 | }
171 | $Test = Get-ARMDeploymentErrorMessage @Parameters
172 | $Test[0] | Should -Be "Error, Find info below:"
173 | $Test[1] | Should -Be "Deployment template validation failed"
174 | }
175 | It "When deployment has a general error, the right results are given" {
176 | Mock Test-AzResourceGroupDeployment -parameterfilter { $Parameters } {
177 | [pscustomobject]@{
178 | Code = 'InvalidTemplateDeployment'
179 | Message = "The template deployment '12345678-1234-1234-1234-12345678abcd' is not valid according to the validation procedure. The
180 | tracking id is '12345678-1234-1234-1234-12345678abcd'. See inner errors for details. Please see https://aka.ms/arm-deploy
181 | for usage details."
182 | }
183 | }
184 | $Mockobject = (Get-Content $PSScriptRoot\MockObjects\Logoutput.json) | ConvertFrom-Json
185 | Mock Get-AzLog {
186 | [object]$Mockobject
187 | }
188 | Mock Start-Sleep { $null }
189 | $Result = Get-ARMDeploymentErrorMessage @Parameters
190 | $Result[0] | Should -Be "the output is a generic error message. The log is searched for a more clear errormessage"
191 | $Result[-3] | Should -Be "General Error. Find info below:"
192 | $Result[-2] | Should -Be "ErrorCode: AccountNameInvalid"
193 | $Result[-1] | Should -Be "Errormessage: s aqkc32cvb2qmmw is not a valid storage account name. Storage account name must be between 3 and 24 characters in length and use numbers and lower-case letters only."
194 | }
195 | It "When no errormessage is found in azurelog, script throws" {
196 | Mock Test-AzResourceGroupDeployment -parameterfilter { $Parameters } {
197 | [pscustomobject]@{
198 | Code = 'InvalidTemplateDeployment'
199 | Message = "The template deployment '12345678-1234-1234-1234-12345678abcd' is not valid according to the validation procedure. The
200 | tracking id is '12345678-1234-1234-1234-12345678abcd'. See inner errors for details. Please see https://aka.ms/arm-deploy
201 | for usage details."
202 | }
203 | }
204 | Mock Get-AzLog {
205 | $null
206 | }
207 | Mock Start-Sleep { $null }
208 | { Get-ARMDeploymentErrorMessage @Parameters } | Should -Throw "Can't get Azure Log Entry. Please check the log manually in the portal."
209 | }
210 | It "Throws when TrowonError is used" {
211 | Mock Test-AzResourceGroupDeployment -parameterfilter { $Parameters } {
212 | [pscustomobject]@{
213 | Code = 'InvalidTemplateDeployment'
214 | Message = "The template deployment '12345678-1234-1234-1234-12345678abcd' is not valid according to the validation procedure. The
215 | tracking id is '12345678-1234-1234-1234-12345678abcd'. See inner errors for details. Please see https://aka.ms/arm-deploy
216 | for usage details."
217 | }
218 | }
219 | $Mockobject = (Get-Content $PSScriptRoot\MockObjects\Logoutput.json) | ConvertFrom-Json
220 | Mock Get-AzLog {
221 | [object]$Mockobject
222 | }
223 | Mock Start-Sleep { $null }
224 | { Get-ARMDeploymentErrorMessage @Parameters -ThrowOnError } | Should -Throw "Deployment is incorrect"
225 |
226 | }
227 | It "All Mocks are called" {
228 | Assert-MockCalled -CommandName Get-AzLog
229 | Assert-MockCalled -CommandName Test-AzResourceGroupDeployment
230 | }
231 | }
232 | }
233 | }
234 |
--------------------------------------------------------------------------------
/Tests/GetHelp.tests.ps1:
--------------------------------------------------------------------------------
1 | $projectRoot = Resolve-Path "$PSScriptRoot\.."
2 | $moduleRoot = Split-Path (Resolve-Path "$projectRoot\*\*.psm1")
3 | $moduleName = Split-Path $moduleRoot -Leaf
4 |
5 | Import-Module (Join-Path $moduleRoot "$moduleName.psd1") -force
6 |
7 | Describe 'Check Comment-based help' {
8 | Context 'All functions should contain Comment-based Help' {
9 | $Commands = (Get-Module ARMHelper).ExportedCommands
10 |
11 | $TestCases = $Commands.Values | Foreach-Object {
12 | @{
13 | Function = $_.Name
14 | }
15 | }
16 |
17 | It " should contain Comment-based Help - Synopsis" -TestCases $TestCases {
18 | param(
19 | $Function
20 | )
21 |
22 | $Help = Get-Help $Function
23 |
24 | $Help.Synopsis | Should -Not -BeNullOrEmpty
25 | }
26 |
27 | It " should contain Comment-based Help - Description" -TestCases $TestCases {
28 | param(
29 | $Function
30 | )
31 |
32 | $Help = Get-Help $Function
33 |
34 | $Help.Description | Should -Not -BeNullOrEmpty
35 | }
36 |
37 | It " should contain Comment-based Help - Synopsis" -TestCases $TestCases {
38 | param(
39 | $Function
40 | )
41 |
42 | $Examples = Get-Help $Function -Examples
43 |
44 | $Examples | Should -Not -BeNullOrEmpty
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/Tests/MockObjects/ExistingResources.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "ResourceId": "/subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/Arm/providers/Microsoft.Compute/disks/SimpleWinVM_disk2_ee1400356e2f458c973b7d7dcecd9b17",
4 | "Id": "/subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/Arm/providers/Microsoft.Compute/disks/SimpleWinVM_disk2_ee1400356e2f458c973b7d7dcecd9b17",
5 | "Identity": null,
6 | "Kind": null,
7 | "Location": "westeurope",
8 | "ManagedBy": "/subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/Arm/providers/Microsoft.Compute/virtualMachines/SimpleWinVM",
9 | "ResourceName": "SimpleWinVM_disk2_ee1400356e2f458c973b7d7dcecd9b17",
10 | "Name": "SimpleWinVM_disk2_ee1400356e2f458c973b7d7dcecd9b17",
11 | "ExtensionResourceName": null,
12 | "ParentResource": null,
13 | "Plan": null,
14 | "Properties": null,
15 | "ResourceGroupName": "Arm",
16 | "Type": "Microsoft.Compute/disks",
17 | "ResourceType": "Microsoft.Compute/disks",
18 | "ExtensionResourceType": null,
19 | "Sku": {
20 | "Name": "Standard_LRS",
21 | "Tier": "Standard",
22 | "Size": null,
23 | "Family": null,
24 | "Model": null,
25 | "Capacity": null
26 | },
27 | "Tags": null,
28 | "SubscriptionId": null,
29 | "CreatedTime": null,
30 | "ChangedTime": null,
31 | "ETag": null
32 | },
33 | {
34 | "ResourceId": "/subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/Arm/providers/Microsoft.Compute/disks/SimpleWinVM_OsDisk_1_f602a12020aa445682f68e35060433da",
35 | "Id": "/subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/Arm/providers/Microsoft.Compute/disks/SimpleWinVM_OsDisk_1_f602a12020aa445682f68e35060433da",
36 | "Identity": null,
37 | "Kind": null,
38 | "Location": "westeurope",
39 | "ManagedBy": "/subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/Arm/providers/Microsoft.Compute/virtualMachines/SimpleWinVM",
40 | "ResourceName": "SimpleWinVM_OsDisk_1_f602a12020aa445682f68e35060433da",
41 | "Name": "SimpleWinVM_OsDisk_1_f602a12020aa445682f68e35060433da",
42 | "ExtensionResourceName": null,
43 | "ParentResource": null,
44 | "Plan": null,
45 | "Properties": null,
46 | "ResourceGroupName": "Arm",
47 | "Type": "Microsoft.Compute/disks",
48 | "ResourceType": "Microsoft.Compute/disks",
49 | "ExtensionResourceType": null,
50 | "Sku": {
51 | "Name": "Standard_LRS",
52 | "Tier": "Standard",
53 | "Size": null,
54 | "Family": null,
55 | "Model": null,
56 | "Capacity": null
57 | },
58 | "Tags": null,
59 | "SubscriptionId": null,
60 | "CreatedTime": null,
61 | "ChangedTime": null,
62 | "ETag": null
63 | },
64 | {
65 | "ResourceId": "/subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/Arm/providers/Microsoft.Compute/virtualMachines/SimpleWinVM",
66 | "Id": "/subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/Arm/providers/Microsoft.Compute/virtualMachines/SimpleWinVM",
67 | "Identity": null,
68 | "Kind": null,
69 | "Location": "westeurope",
70 | "ManagedBy": null,
71 | "ResourceName": "SimpleWinVM",
72 | "Name": "SimpleWinVM",
73 | "ExtensionResourceName": null,
74 | "ParentResource": null,
75 | "Plan": null,
76 | "Properties": null,
77 | "ResourceGroupName": "Arm",
78 | "Type": "Microsoft.Compute/virtualMachines",
79 | "ResourceType": "Microsoft.Compute/virtualMachines",
80 | "ExtensionResourceType": null,
81 | "Sku": null,
82 | "Tags": null,
83 | "SubscriptionId": null,
84 | "CreatedTime": null,
85 | "ChangedTime": null,
86 | "ETag": null
87 | },
88 | {
89 | "ResourceId": "/subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/Arm/providers/Microsoft.Network/networkInterfaces/myVMNic",
90 | "Id": "/subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/Arm/providers/Microsoft.Network/networkInterfaces/myVMNic",
91 | "Identity": null,
92 | "Kind": null,
93 | "Location": "westeurope",
94 | "ManagedBy": null,
95 | "ResourceName": "myVMNic",
96 | "Name": "myVMNic",
97 | "ExtensionResourceName": null,
98 | "ParentResource": null,
99 | "Plan": null,
100 | "Properties": null,
101 | "ResourceGroupName": "Arm",
102 | "Type": "Microsoft.Network/networkInterfaces",
103 | "ResourceType": "Microsoft.Network/networkInterfaces",
104 | "ExtensionResourceType": null,
105 | "Sku": null,
106 | "Tags": null,
107 | "SubscriptionId": null,
108 | "CreatedTime": null,
109 | "ChangedTime": null,
110 | "ETag": null
111 | },
112 | {
113 | "ResourceId": "/subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/Arm/providers/Microsoft.Network/publicIPAddresses/myPublicIP",
114 | "Id": "/subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/Arm/providers/Microsoft.Network/publicIPAddresses/myPublicIP",
115 | "Identity": null,
116 | "Kind": null,
117 | "Location": "westeurope",
118 | "ManagedBy": null,
119 | "ResourceName": "myPublicIP",
120 | "Name": "myPublicIP",
121 | "ExtensionResourceName": null,
122 | "ParentResource": null,
123 | "Plan": null,
124 | "Properties": null,
125 | "ResourceGroupName": "Arm",
126 | "Type": "Microsoft.Network/publicIPAddresses",
127 | "ResourceType": "Microsoft.Network/publicIPAddresses",
128 | "ExtensionResourceType": null,
129 | "Sku": {
130 | "Name": "Basic",
131 | "Tier": "Regional",
132 | "Size": null,
133 | "Family": null,
134 | "Model": null,
135 | "Capacity": null
136 | },
137 | "Tags": null,
138 | "SubscriptionId": null,
139 | "CreatedTime": null,
140 | "ChangedTime": null,
141 | "ETag": null
142 | },
143 | {
144 | "ResourceId": "/subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/Arm/providers/Microsoft.Network/virtualNetworks/MyVNET",
145 | "Id": "/subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/Arm/providers/Microsoft.Network/virtualNetworks/MyVNET",
146 | "Identity": null,
147 | "Kind": null,
148 | "Location": "westeurope",
149 | "ManagedBy": null,
150 | "ResourceName": "MyVNET",
151 | "Name": "MyVNET",
152 | "ExtensionResourceName": null,
153 | "ParentResource": null,
154 | "Plan": null,
155 | "Properties": null,
156 | "ResourceGroupName": "Arm",
157 | "Type": "Microsoft.Network/virtualNetworks",
158 | "ResourceType": "Microsoft.Network/virtualNetworks",
159 | "ExtensionResourceType": null,
160 | "Sku": null,
161 | "Tags": null,
162 | "SubscriptionId": null,
163 | "CreatedTime": null,
164 | "ChangedTime": null,
165 | "ETag": null
166 | },
167 | {
168 | "ResourceId": "/subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/Arm/providers/Microsoft.Storage/storageAccounts/mstaqkc32c2qmmw",
169 | "Id": "/subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/Arm/providers/Microsoft.Storage/storageAccounts/mstaqkc32c2qmmw",
170 | "Identity": null,
171 | "Kind": "Storage",
172 | "Location": "westeurope",
173 | "ManagedBy": null,
174 | "ResourceName": "mstaqkc32c2qmmw",
175 | "Name": "mstaqkc32c2qmmw",
176 | "ExtensionResourceName": null,
177 | "ParentResource": null,
178 | "Plan": null,
179 | "Properties": null,
180 | "ResourceGroupName": "Arm",
181 | "Type": "Microsoft.Storage/storageAccounts",
182 | "ResourceType": "Microsoft.Storage/storageAccounts",
183 | "ExtensionResourceType": null,
184 | "Sku": {
185 | "Name": "Standard_LRS",
186 | "Tier": "Standard",
187 | "Size": null,
188 | "Family": null,
189 | "Model": null,
190 | "Capacity": null
191 | },
192 | "Tags": {},
193 | "SubscriptionId": null,
194 | "CreatedTime": null,
195 | "ChangedTime": null,
196 | "ETag": null
197 | }
198 | ]
199 |
--------------------------------------------------------------------------------
/Tests/MockObjects/ExistingResourcesDeleted.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "ResourceId": "/subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/ARMtesting/providers/Microsoft.Storage/storageAccounts/testaegewteawgfaef",
4 | "Id": "/subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/ARMtesting/providers/Microsoft.Storage/storageAccounts/testaegewteawgfaef",
5 | "Identity": null,
6 | "Kind": "StorageV2",
7 | "Location": "westeurope",
8 | "ManagedBy": null,
9 | "ResourceName": "testaegewteawgfaef",
10 | "Name": "testaegewteawgfaef",
11 | "ExtensionResourceName": null,
12 | "ParentResource": null,
13 | "Plan": null,
14 | "Properties": null,
15 | "ResourceGroupName": "ARM",
16 | "Type": "Microsoft.Storage/storageAccounts",
17 | "ResourceType": "Microsoft.Storage/storageAccounts",
18 | "ExtensionResourceType": null,
19 | "Sku": {
20 | "Name": "Standard_LRS",
21 | "Tier": "Standard",
22 | "Size": null,
23 | "Family": null,
24 | "Model": null,
25 | "Capacity": null
26 | },
27 | "Tags": {},
28 | "SubscriptionId": null,
29 | "CreatedTime": null,
30 | "ChangedTime": null,
31 | "ETag": null
32 | }
33 | ]
34 |
--------------------------------------------------------------------------------
/Tests/MockObjects/ExistingResourcesdiffRG.json:
--------------------------------------------------------------------------------
1 | [
2 |
3 | {
4 | "ResourceId": "/subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/Armtesting/providers/Microsoft.Compute/virtualMachines/SimpleWinVM",
5 | "Id": "/subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/Armtesting/providers/Microsoft.Compute/virtualMachines/SimpleWinVM",
6 | "Identity": null,
7 | "Kind": null,
8 | "Location": "westeurope",
9 | "ManagedBy": null,
10 | "ResourceName": "SimpleWinVM",
11 | "Name": "SimpleWinVM",
12 | "ExtensionResourceName": null,
13 | "ParentResource": null,
14 | "Plan": null,
15 | "Properties": null,
16 | "ResourceGroupName": "Armtesting",
17 | "Type": "Microsoft.Compute/virtualMachines",
18 | "ResourceType": "Microsoft.Compute/virtualMachines",
19 | "ExtensionResourceType": null,
20 | "Sku": null,
21 | "Tags": null,
22 | "SubscriptionId": null,
23 | "CreatedTime": null,
24 | "ChangedTime": null,
25 | "ETag": null
26 | },
27 | {
28 | "ResourceId": "/subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/Armtesting/providers/Microsoft.Network/networkInterfaces/myVMNic",
29 | "Id": "/subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/Armtesting/providers/Microsoft.Network/networkInterfaces/myVMNic",
30 | "Identity": null,
31 | "Kind": null,
32 | "Location": "westeurope",
33 | "ManagedBy": null,
34 | "ResourceName": "myVMNic",
35 | "Name": "myVMNic",
36 | "ExtensionResourceName": null,
37 | "ParentResource": null,
38 | "Plan": null,
39 | "Properties": null,
40 | "ResourceGroupName": "Armtesting",
41 | "Type": "Microsoft.Network/networkInterfaces",
42 | "ResourceType": "Microsoft.Network/networkInterfaces",
43 | "ExtensionResourceType": null,
44 | "Sku": null,
45 | "Tags": null,
46 | "SubscriptionId": null,
47 | "CreatedTime": null,
48 | "ChangedTime": null,
49 | "ETag": null
50 | },
51 | {
52 | "ResourceId": "/subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/Armtesting/providers/Microsoft.Network/publicIPAddresses/myPublicIP",
53 | "Id": "/subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/Armtesting/providers/Microsoft.Network/publicIPAddresses/myPublicIP",
54 | "Identity": null,
55 | "Kind": null,
56 | "Location": "westeurope",
57 | "ManagedBy": null,
58 | "ResourceName": "myPublicIP",
59 | "Name": "myPublicIP",
60 | "ExtensionResourceName": null,
61 | "ParentResource": null,
62 | "Plan": null,
63 | "Properties": null,
64 | "ResourceGroupName": "Armtesting",
65 | "Type": "Microsoft.Network/publicIPAddresses",
66 | "ResourceType": "Microsoft.Network/publicIPAddresses",
67 | "ExtensionResourceType": null,
68 | "Sku": {
69 | "Name": "Basic",
70 | "Tier": "Regional",
71 | "Size": null,
72 | "Family": null,
73 | "Model": null,
74 | "Capacity": null
75 | },
76 | "Tags": null,
77 | "SubscriptionId": null,
78 | "CreatedTime": null,
79 | "ChangedTime": null,
80 | "ETag": null
81 | },
82 | {
83 | "ResourceId": "/subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/Armtesting/providers/Microsoft.Network/virtualNetworks/MyVNET",
84 | "Id": "/subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/Armtesting/providers/Microsoft.Network/virtualNetworks/MyVNET",
85 | "Identity": null,
86 | "Kind": null,
87 | "Location": "westeurope",
88 | "ManagedBy": null,
89 | "ResourceName": "MyVNET",
90 | "Name": "MyVNET",
91 | "ExtensionResourceName": null,
92 | "ParentResource": null,
93 | "Plan": null,
94 | "Properties": null,
95 | "ResourceGroupName": "Armtesting",
96 | "Type": "Microsoft.Network/virtualNetworks",
97 | "ResourceType": "Microsoft.Network/virtualNetworks",
98 | "ExtensionResourceType": null,
99 | "Sku": null,
100 | "Tags": null,
101 | "SubscriptionId": null,
102 | "CreatedTime": null,
103 | "ChangedTime": null,
104 | "ETag": null
105 | },
106 |
107 | ]
108 |
--------------------------------------------------------------------------------
/Tests/MockObjects/Logoutput.json:
--------------------------------------------------------------------------------
1 | {
2 | "Properties": {
3 | "Content": {
4 | "statusCode": "BadRequest",
5 | "serviceRequestId": null,
6 | "statusMessage": "{\"error\":{\"code\":\"InvalidTemplateDeployment\",\"message\":\"The template deployment 'f0f1ac32-fdc6-44ba-90c7-2aed1a84388e' is not valid according to the validation procedure. The tracking id is 'a9ec07b0-86be-429b-b8a5-c5b2dd32d003'. See inner errors for details. Please see https://aka.ms/arm-deploy for usage details.\",\"details\":[{\"code\":\"PreflightValidationCheckFailed\",\"message\":\"Preflight validation failed. Please refer to the details for the specific errors.\",\"details\":[{\"code\":\"AccountNameInvalid\",\"target\":\"s aqkc32cvb2qmmw\",\"message\":\"s aqkc32cvb2qmmw is not a valid storage account name. Storage account name must be between 3 and 24 characters in length and use numbers and lower-case letters only.\"}]}]}}"
7 | }
8 | }
9 | }
--------------------------------------------------------------------------------
/Tests/MockObjects/NestedResult.json:
--------------------------------------------------------------------------------
1 | {
2 | "templateHash": "12342355",
3 | "parameters": {
4 | "adminUsername": {
5 | "type": "String",
6 | "value": "henk"
7 | },
8 | "adminPassword": {
9 | "type": "SecureString"
10 | },
11 | "dnsLabelPrefix": {
12 | "type": "String",
13 | "value": "b233523523la"
14 | },
15 | "uri": {
16 | "type": "String",
17 | "value": "https://example.blob.core.windows.net"
18 | },
19 | "sas": {
20 | "type": "SecureString"
21 | },
22 | "windowsOSVersion": {
23 | "type": "String",
24 | "value": "2016-Datacenter"
25 | },
26 | "location": {
27 | "type": "String",
28 | "value": "westeurope"
29 | }
30 | },
31 | "mode": "Complete",
32 | "provisioningState": "Succeeded",
33 | "timestamp": "2019-09-05T16:49:13.4122369Z",
34 | "duration": "PT0S",
35 | "correlationId": "7034ffb6-3b17-4ab6-b289-5899ad46e576",
36 | "providers": [
37 | {
38 | "namespace": "Microsoft.Resources",
39 | "resourceTypes": ""
40 | },
41 | {
42 | "namespace": "Microsoft.Network",
43 | "resourceTypes": " "
44 | },
45 | {
46 | "namespace": "Microsoft.Compute",
47 | "resourceTypes": ""
48 | }
49 | ],
50 | "dependencies": [
51 | {
52 | "dependsOn": " ",
53 | "id": "/subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/Armtesting/providers/Microsoft.Network/networkInterfaces/myVMNic",
54 | "resourceType": "Microsoft.Network/networkInterfaces",
55 | "resourceName": "myVMNic"
56 | },
57 | {
58 | "dependsOn": " ",
59 | "id": "/subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/Armtesting/providers/Microsoft.Compute/virtualMachines/SimpleWinVM",
60 | "resourceType": "Microsoft.Compute/virtualMachines",
61 | "resourceName": "SimpleWinVM"
62 | }
63 | ],
64 | "validatedResources": [
65 | {
66 | "apiVersion": "2018-05-01",
67 | "id": "/subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/Armtesting/providers/Microsoft.Resources/Deployments/linkedTemplate",
68 | "name": "linkedTemplate",
69 | "type": "Microsoft.Resources/Deployments",
70 | "properties": "@{mode=Incremental; templateLink=; parameters=}"
71 | },
72 | {
73 | "apiVersion": "2018-11-01",
74 | "id": "/subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/Armtesting/providers/Microsoft.Network/publicIPAddresses/myPublicIP",
75 | "name": "myPublicIP",
76 | "type": "Microsoft.Network/publicIPAddresses",
77 | "location": "westeurope",
78 | "properties": "@{publicIPAllocationMethod=Dynamic; dnsSettings=}"
79 | },
80 | {
81 | "apiVersion": "2018-11-01",
82 | "id": "/subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/Armtesting/providers/Microsoft.Network/virtualNetworks/MyVNET",
83 | "name": "MyVNET",
84 | "type": "Microsoft.Network/virtualNetworks",
85 | "location": "westeurope",
86 | "properties": "@{addressSpace=; subnets=System.Object[]}"
87 | },
88 | {
89 | "dependsOn": "/subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/ArmTesting/providers/Microsoft.Network/publicIPAddresses/myPublicIP /subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/ArmTesting/providers/Microsoft.Network/virtualNetworks/MyVNET",
90 | "apiVersion": "2018-11-01",
91 | "id": "/subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/Armtesting/providers/Microsoft.Network/networkInterfaces/myVMNic",
92 | "name": "myVMNic",
93 | "type": "Microsoft.Network/networkInterfaces",
94 | "location": "westeurope",
95 | "properties": "@{ipConfigurations=System.Object[]}"
96 | },
97 | {
98 | "dependsOn": "/subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/Armtesting/providers/Microsoft.Resources/Deployments/linkedTemplate /subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/ArmTesting/providers/Microsoft.Network/networkInterfaces/myVMNic",
99 | "apiVersion": "2018-10-01",
100 | "id": "/subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/Armtesting/providers/Microsoft.Compute/virtualMachines/SimpleWinVM",
101 | "name": "SimpleWinVM",
102 | "type": "Microsoft.Compute/virtualMachines",
103 | "location": "westeurope",
104 | "properties": "@{hardwareProfile=; osProfile=; storageProfile=; networkProfile=; diagnosticsProfile=}"
105 | },
106 | {
107 | "apiVersion": "2018-11-01",
108 | "id": "/subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/ArmTesting/providers/Microsoft.Storage/storageAccounts/123",
109 | "name": "ackjvgp3y3huksawinvm",
110 | "type": "Microsoft.Storage/storageAccounts",
111 | "sku": "@{name=Standard_LRS}",
112 | "kind": "Storage",
113 | "location": "westeurope",
114 | "properties": ""
115 | }
116 | ]
117 | }
118 |
--------------------------------------------------------------------------------
/Tests/MockObjects/Result.json:
--------------------------------------------------------------------------------
1 | {
2 | "templateHash": "1220693435534564543",
3 | "parameters": {
4 | "adminUsername": {
5 | "type": "String",
6 | "value": "Henk"
7 | },
8 | "adminPassword": {
9 | "type": "SecureString"
10 | },
11 | "dnsLabelPrefix": {
12 | "type": "String",
13 | "value": "eandomrlkajelnadada"
14 | },
15 | "windowsOSVersion": {
16 | "type": "String",
17 | "value": "2016-Datacenter"
18 | },
19 | "location": {
20 | "type": "String",
21 | "value": "westeurope"
22 | }
23 | },
24 | "mode": "Incremental",
25 | "provisioningState": "Succeeded",
26 | "timestamp": "2019-07-05T20:45:58.2062472Z",
27 | "duration": "PT0S",
28 | "correlationId": "93690b-4ba0-461b-a5da-dc7b0",
29 | "providers": [
30 | {
31 | "namespace": "Microsoft.Storage",
32 | "resourceTypes": [
33 | {
34 | "resourceType": "storageAccounts",
35 | "locations": [
36 | "westeurope"
37 | ]
38 | }
39 | ]
40 | },
41 | {
42 | "namespace": "Microsoft.Network",
43 | "resourceTypes": [
44 | {
45 | "resourceType": "publicIPAddresses",
46 | "locations": [
47 | "westeurope"
48 | ]
49 | },
50 | {
51 | "resourceType": "virtualNetworks",
52 | "locations": [
53 | "westeurope"
54 | ]
55 | },
56 | {
57 | "resourceType": "networkInterfaces",
58 | "locations": [
59 | "westeurope"
60 | ]
61 | }
62 | ]
63 | },
64 | {
65 | "namespace": "Microsoft.Compute",
66 | "resourceTypes": [
67 | {
68 | "resourceType": "virtualMachines",
69 | "locations": [
70 | "westeurope"
71 | ]
72 | }
73 | ]
74 | }
75 | ],
76 | "dependencies": [
77 | {
78 | "dependsOn": [
79 | {
80 | "id": "/subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/ARMtesting/providers/Microsoft.Network/publicIPAddresses/myPublicIP",
81 | "resourceType": "Microsoft.Network/publicIPAddresses",
82 | "resourceName": "myPublicIP"
83 | },
84 | {
85 | "id": "/subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/ARMtesting/providers/Microsoft.Network/virtualNetworks/MyVNET",
86 | "resourceType": "Microsoft.Network/virtualNetworks",
87 | "resourceName": "MyVNET"
88 | }
89 | ],
90 | "id": "/subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/armtesting/providers/Microsoft.Network/networkInterfaces/myVMNic",
91 | "resourceType": "Microsoft.Network/networkInterfaces",
92 | "resourceName": "myVMNic"
93 | },
94 | {
95 | "dependsOn": [
96 | {
97 | "id": "/subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/ARMtesting/providers/Microsoft.Storage/storageAccounts/qkc32cvb2qmmwsawinvm",
98 | "resourceType": "Microsoft.Storage/storageAccounts",
99 | "resourceName": "qkc32cvb2qmmwsawinvm"
100 | },
101 | {
102 | "id": "/subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/ARMtesting/providers/Microsoft.Network/networkInterfaces/myVMNic",
103 | "resourceType": "Microsoft.Network/networkInterfaces",
104 | "resourceName": "myVMNic"
105 | }
106 | ],
107 | "id": "/subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/armtesting/providers/Microsoft.Compute/virtualMachines/SimpleWinVM",
108 | "resourceType": "Microsoft.Compute/virtualMachines",
109 | "resourceName": "SimpleWinVM"
110 | }
111 | ],
112 | "validatedResources": [
113 | {
114 | "apiVersion": "2018-11-01",
115 | "id": "/subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/armtesting/providers/Microsoft.Storage/storageAccounts/qkc32cvb2qmmwsawinvm",
116 | "name": "qkc32cvb2qmmwsawinvm",
117 | "type": "Microsoft.Storage/storageAccounts",
118 | "sku": {
119 | "name": "Standard_LRS"
120 | },
121 | "kind": "Storage",
122 | "location": "westeurope",
123 | "properties": {
124 |
125 | }
126 | },
127 | {
128 | "apiVersion": "2018-11-01",
129 | "id": "/subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/armtesting/providers/Microsoft.Network/publicIPAddresses/myPublicIP",
130 | "name": "myPublicIP",
131 | "type": "Microsoft.Network/publicIPAddresses",
132 | "location": "westeurope",
133 | "properties": {
134 | "publicIPAllocationMethod": "Dynamic",
135 | "dnsSettings": {
136 | "domainNameLabel": "eandomrlkajelnadada"
137 | }
138 | }
139 | },
140 | {
141 | "apiVersion": "2018-11-01",
142 | "id": "/subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/armtesting/providers/Microsoft.Network/virtualNetworks/MyVNET",
143 | "name": "MyVNET",
144 | "type": "Microsoft.Network/virtualNetworks",
145 | "location": "westeurope",
146 | "properties": {
147 | "addressSpace": {
148 | "addressPrefixes": [
149 | "10.0.0.0/16"
150 | ]
151 | },
152 | "subnets": [
153 | {
154 | "name": "Subnet",
155 | "properties": {
156 | "addressPrefix": "10.0.0.0/24"
157 | }
158 | }
159 | ]
160 | }
161 | },
162 | {
163 | "dependsOn": [
164 | "/subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/ARMtesting/providers/Microsoft.Network/publicIPAddresses/myPublicIP",
165 | "/subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/ARMtesting/providers/Microsoft.Network/virtualNetworks/MyVNET"
166 | ],
167 | "apiVersion": "2018-11-01",
168 | "id": "/subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/armtesting/providers/Microsoft.Network/networkInterfaces/myVMNic",
169 | "name": "myVMNic",
170 | "type": "Microsoft.Network/networkInterfaces",
171 | "location": "westeurope",
172 | "properties": {
173 | "ipConfigurations": [
174 | {
175 | "name": "ipconfig1",
176 | "properties": {
177 | "privateIPAllocationMethod": "Dynamic",
178 | "publicIPAddress": {
179 | "id": "/subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/ARMtesting/providers/Microsoft.Network/publicIPAddresses/myPublicIP"
180 | },
181 | "subnet": {
182 | "id": "/subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/ARMtesting/providers/Microsoft.Network/virtualNetworks/MyVNET/subnets/Subnet"
183 | }
184 | }
185 | }
186 | ]
187 | }
188 | },
189 | {
190 | "dependsOn": [
191 | "/subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/ARMtesting/providers/Microsoft.Storage/storageAccounts/qkc32cvb2qmmwsawinvm",
192 | "/subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/ARMtesting/providers/Microsoft.Network/networkInterfaces/myVMNic"
193 | ],
194 | "apiVersion": "2018-10-01",
195 | "id": "/subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/armtesting/providers/Microsoft.Compute/virtualMachines/SimpleWinVM",
196 | "name": "SimpleWinVM",
197 | "type": "Microsoft.Compute/virtualMachines",
198 | "location": "westeurope",
199 | "properties": {
200 | "hardwareProfile": {
201 | "vmSize": "Standard_A2"
202 | },
203 | "osProfile": {
204 | "computerName": "SimpleWinVM",
205 | "adminUsername": "Henk",
206 | "adminPassword": "Welkom123"
207 | },
208 | "storageProfile": {
209 | "imageReference": {
210 | "publisher": "MicrosoftWindowsServer",
211 | "offer": "WindowsServer",
212 | "sku": "2016-Datacenter",
213 | "version": "latest"
214 | },
215 | "osDisk": {
216 | "createOption": "FromImage"
217 | },
218 | "dataDisks": [
219 | {
220 | "diskSizeGB": 1023,
221 | "lun": 0,
222 | "createOption": "Empty"
223 | }
224 | ]
225 | },
226 | "networkProfile": {
227 | "networkInterfaces": [
228 | {
229 | "id": "/subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/ARMtesting/providers/Microsoft.Network/networkInterfaces/myVMNic"
230 | }
231 | ]
232 | },
233 | "diagnosticsProfile": {
234 | "bootDiagnostics": {
235 | "enabled": true,
236 | "storageUri": "[reference(resourceId(\u0027Microsoft.Storage/storageAccounts/\u0027, variables(\u0027storageAccountName\u0027))).primaryEndpoints.blob]"
237 | }
238 | }
239 | }
240 | }
241 | ]
242 | }
--------------------------------------------------------------------------------
/Tests/MockObjects/ResultComplete.json:
--------------------------------------------------------------------------------
1 | {
2 | "templateHash": "1220693435534564543",
3 | "parameters": {
4 | "adminUsername": {
5 | "type": "String",
6 | "value": "Henk"
7 | },
8 | "adminPassword": {
9 | "type": "SecureString"
10 | },
11 | "dnsLabelPrefix": {
12 | "type": "String",
13 | "value": "eandomrlkajelnadada"
14 | },
15 | "windowsOSVersion": {
16 | "type": "String",
17 | "value": "2016-Datacenter"
18 | },
19 | "location": {
20 | "type": "String",
21 | "value": "westeurope"
22 | }
23 | },
24 | "mode": "Complete",
25 | "provisioningState": "Succeeded",
26 | "timestamp": "2019-07-05T20:45:58.2062472Z",
27 | "duration": "PT0S",
28 | "correlationId": "9363190b-4ba0-461b-a5da-dc736bb382b0",
29 | "providers": [
30 | {
31 | "namespace": "Microsoft.Storage",
32 | "resourceTypes": [
33 | {
34 | "resourceType": "storageAccounts",
35 | "locations": [
36 | "westeurope"
37 | ]
38 | }
39 | ]
40 | },
41 | {
42 | "namespace": "Microsoft.Network",
43 | "resourceTypes": [
44 | {
45 | "resourceType": "publicIPAddresses",
46 | "locations": [
47 | "westeurope"
48 | ]
49 | },
50 | {
51 | "resourceType": "virtualNetworks",
52 | "locations": [
53 | "westeurope"
54 | ]
55 | },
56 | {
57 | "resourceType": "networkInterfaces",
58 | "locations": [
59 | "westeurope"
60 | ]
61 | }
62 | ]
63 | },
64 | {
65 | "namespace": "Microsoft.Compute",
66 | "resourceTypes": [
67 | {
68 | "resourceType": "virtualMachines",
69 | "locations": [
70 | "westeurope"
71 | ]
72 | }
73 | ]
74 | }
75 | ],
76 | "dependencies": [
77 | {
78 | "dependsOn": [
79 | {
80 | "id": "/subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/ARMtesting/providers/Microsoft.Network/publicIPAddresses/myPublicIP",
81 | "resourceType": "Microsoft.Network/publicIPAddresses",
82 | "resourceName": "myPublicIP"
83 | },
84 | {
85 | "id": "/subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/ARMtesting/providers/Microsoft.Network/virtualNetworks/MyVNET",
86 | "resourceType": "Microsoft.Network/virtualNetworks",
87 | "resourceName": "MyVNET"
88 | }
89 | ],
90 | "id": "/subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/armtesting/providers/Microsoft.Network/networkInterfaces/myVMNic",
91 | "resourceType": "Microsoft.Network/networkInterfaces",
92 | "resourceName": "myVMNic"
93 | },
94 | {
95 | "dependsOn": [
96 | {
97 | "id": "/subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/ARMtesting/providers/Microsoft.Storage/storageAccounts/qkc32cvb2qmmwsawinvm",
98 | "resourceType": "Microsoft.Storage/storageAccounts",
99 | "resourceName": "qkc32cvb2qmmwsawinvm"
100 | },
101 | {
102 | "id": "/subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/ARMtesting/providers/Microsoft.Network/networkInterfaces/myVMNic",
103 | "resourceType": "Microsoft.Network/networkInterfaces",
104 | "resourceName": "myVMNic"
105 | }
106 | ],
107 | "id": "/subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/armtesting/providers/Microsoft.Compute/virtualMachines/SimpleWinVM",
108 | "resourceType": "Microsoft.Compute/virtualMachines",
109 | "resourceName": "SimpleWinVM"
110 | }
111 | ],
112 | "validatedResources": [
113 | {
114 | "apiVersion": "2018-11-01",
115 | "id": "/subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/armtesting/providers/Microsoft.Storage/storageAccounts/qkc32cvb2qmmwsawinvm",
116 | "name": "qkc32cvb2qmmwsawinvm",
117 | "type": "Microsoft.Storage/storageAccounts",
118 | "sku": {
119 | "name": "Standard_LRS"
120 | },
121 | "kind": "Storage",
122 | "location": "westeurope",
123 | "properties": {
124 |
125 | }
126 | },
127 | {
128 | "apiVersion": "2018-11-01",
129 | "id": "/subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/armtesting/providers/Microsoft.Network/publicIPAddresses/myPublicIP",
130 | "name": "myPublicIP",
131 | "type": "Microsoft.Network/publicIPAddresses",
132 | "location": "westeurope",
133 | "properties": {
134 | "publicIPAllocationMethod": "Dynamic",
135 | "dnsSettings": {
136 | "domainNameLabel": "eandomrlkajelnadada"
137 | }
138 | }
139 | },
140 | {
141 | "apiVersion": "2018-11-01",
142 | "id": "/subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/armtesting/providers/Microsoft.Network/virtualNetworks/MyVNET",
143 | "name": "MyVNET",
144 | "type": "Microsoft.Network/virtualNetworks",
145 | "location": "westeurope",
146 | "properties": {
147 | "addressSpace": {
148 | "addressPrefixes": [
149 | "10.0.0.0/16"
150 | ]
151 | },
152 | "subnets": [
153 | {
154 | "name": "Subnet",
155 | "properties": {
156 | "addressPrefix": "10.0.0.0/24"
157 | }
158 | }
159 | ]
160 | }
161 | },
162 | {
163 | "dependsOn": [
164 | "/subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/ARMtesting/providers/Microsoft.Network/publicIPAddresses/myPublicIP",
165 | "/subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/ARMtesting/providers/Microsoft.Network/virtualNetworks/MyVNET"
166 | ],
167 | "apiVersion": "2018-11-01",
168 | "id": "/subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/armtesting/providers/Microsoft.Network/networkInterfaces/myVMNic",
169 | "name": "myVMNic",
170 | "type": "Microsoft.Network/networkInterfaces",
171 | "location": "westeurope",
172 | "properties": {
173 | "ipConfigurations": [
174 | {
175 | "name": "ipconfig1",
176 | "properties": {
177 | "privateIPAllocationMethod": "Dynamic",
178 | "publicIPAddress": {
179 | "id": "/subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/ARMtesting/providers/Microsoft.Network/publicIPAddresses/myPublicIP"
180 | },
181 | "subnet": {
182 | "id": "/subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/ARMtesting/providers/Microsoft.Network/virtualNetworks/MyVNET/subnets/Subnet"
183 | }
184 | }
185 | }
186 | ]
187 | }
188 | },
189 | {
190 | "dependsOn": [
191 | "/subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/ARMtesting/providers/Microsoft.Storage/storageAccounts/qkc32cvb2qmmwsawinvm",
192 | "/subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/ARMtesting/providers/Microsoft.Network/networkInterfaces/myVMNic"
193 | ],
194 | "apiVersion": "2018-10-01",
195 | "id": "/subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/armtesting/providers/Microsoft.Compute/virtualMachines/SimpleWinVM",
196 | "name": "SimpleWinVM",
197 | "type": "Microsoft.Compute/virtualMachines",
198 | "location": "westeurope",
199 | "properties": {
200 | "hardwareProfile": {
201 | "vmSize": "Standard_A2"
202 | },
203 | "osProfile": {
204 | "computerName": "SimpleWinVM",
205 | "adminUsername": "Henk",
206 | "adminPassword": "Welkom123"
207 | },
208 | "storageProfile": {
209 | "imageReference": {
210 | "publisher": "MicrosoftWindowsServer",
211 | "offer": "WindowsServer",
212 | "sku": "2016-Datacenter",
213 | "version": "latest"
214 | },
215 | "osDisk": {
216 | "createOption": "FromImage"
217 | },
218 | "dataDisks": [
219 | {
220 | "diskSizeGB": 1023,
221 | "lun": 0,
222 | "createOption": "Empty"
223 | }
224 | ]
225 | },
226 | "networkProfile": {
227 | "networkInterfaces": [
228 | {
229 | "id": "/subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/ARMtesting/providers/Microsoft.Network/networkInterfaces/myVMNic"
230 | }
231 | ]
232 | },
233 | "diagnosticsProfile": {
234 | "bootDiagnostics": {
235 | "enabled": true,
236 | "storageUri": "[reference(resourceId(\u0027Microsoft.Storage/storageAccounts/\u0027, variables(\u0027storageAccountName\u0027))).primaryEndpoints.blob]"
237 | }
238 | }
239 | }
240 | }
241 | ]
242 | }
--------------------------------------------------------------------------------
/Tests/MockObjects/ResultCompleteaz.json:
--------------------------------------------------------------------------------
1 | {
2 | "templateHash": "1220693435534564543",
3 | "parameters": {
4 | "adminUsername": {
5 | "type": "String",
6 | "value": "Test"
7 | },
8 | "adminPassword": {
9 | "type": "SecureString"
10 | },
11 | "dnsLabelPrefix": {
12 | "type": "String",
13 | "value": "eandomrlkajelnadada"
14 | },
15 | "windowsOSVersion": {
16 | "type": "String",
17 | "value": "2016-Datacenter"
18 | },
19 | "location": {
20 | "type": "String",
21 | "value": "westeurope"
22 | }
23 | },
24 | "mode": "Complete",
25 | "provisioningState": "Succeeded",
26 | "timestamp": "2019-07-05T20:45:58.2062472Z",
27 | "duration": "PT0S",
28 | "correlationId": "93690b-4ba0-461b-a5da-dc7b0",
29 | "providers": [
30 | {
31 | "namespace": "Microsoft.Storage",
32 | "resourceTypes": ""
33 | },
34 | {
35 | "namespace": "Microsoft.Network",
36 | "resourceTypes": " "
37 | },
38 | {
39 | "namespace": "Microsoft.Compute",
40 | "resourceTypes": ""
41 | }
42 | ],
43 | "dependencies": [
44 | {
45 | "dependsOn": " ",
46 | "id": "/subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/armtesting/providers/Microsoft.Network/networkInterfaces/myVMNic",
47 | "resourceType": "Microsoft.Network/networkInterfaces",
48 | "resourceName": "myVMNic"
49 | },
50 | {
51 | "dependsOn": " ",
52 | "id": "/subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/armtesting/providers/Microsoft.Compute/virtualMachines/SimpleWinVM",
53 | "resourceType": "Microsoft.Compute/virtualMachines",
54 | "resourceName": "SimpleWinVM"
55 | }
56 | ],
57 | "validatedResources": [
58 | {
59 | "id": "/subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/armtesting/providers/Microsoft.Storage/storageAccounts/mstaqkc32c2qmmw"
60 | },
61 | {
62 | "id": "/subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/armtesting/providers/Microsoft.Network/publicIPAddresses/myPublicIP"
63 | },
64 | {
65 | "id": "/subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/armtesting/providers/Microsoft.Network/virtualNetworks/MyVNET"
66 | },
67 | {
68 | "id": "/subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/armtesting/providers/Microsoft.Network/networkInterfaces/myVMNic"
69 | },
70 | {
71 | "id": "/subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/armtesting/providers/Microsoft.Compute/virtualMachines/SimpleWinVM"
72 | }
73 | ]
74 | }
75 |
--------------------------------------------------------------------------------
/Tests/MockObjects/Resultaz.json:
--------------------------------------------------------------------------------
1 | {
2 | "templateHash": "1220693435534564543",
3 | "parameters": {
4 | "adminUsername": {
5 | "type": "String",
6 | "value": "Test"
7 | },
8 | "adminPassword": {
9 | "type": "SecureString"
10 | },
11 | "dnsLabelPrefix": {
12 | "type": "String",
13 | "value": "eandomrlkajelnadada"
14 | },
15 | "windowsOSVersion": {
16 | "type": "String",
17 | "value": "2016-Datacenter"
18 | },
19 | "location": {
20 | "type": "String",
21 | "value": "westeurope"
22 | }
23 | },
24 | "mode": "Incremental",
25 | "provisioningState": "Succeeded",
26 | "timestamp": "2019-07-05T20:45:58.2062472Z",
27 | "duration": "PT0S",
28 | "correlationId": "93690b-4ba0-461b-a5da-dc7b0",
29 | "providers": [
30 | {
31 | "namespace": "Microsoft.Storage",
32 | "resourceTypes": ""
33 | },
34 | {
35 | "namespace": "Microsoft.Network",
36 | "resourceTypes": " "
37 | },
38 | {
39 | "namespace": "Microsoft.Compute",
40 | "resourceTypes": ""
41 | }
42 | ],
43 | "dependencies": [
44 | {
45 | "dependsOn": " ",
46 | "id": "/subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/armtesting/providers/Microsoft.Network/networkInterfaces/myVMNic",
47 | "resourceType": "Microsoft.Network/networkInterfaces",
48 | "resourceName": "myVMNic"
49 | },
50 | {
51 | "dependsOn": " ",
52 | "id": "/subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/armtesting/providers/Microsoft.Compute/virtualMachines/SimpleWinVM",
53 | "resourceType": "Microsoft.Compute/virtualMachines",
54 | "resourceName": "SimpleWinVM"
55 | }
56 | ],
57 | "validatedResources": [
58 | {
59 | "id": "/subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/armtesting/providers/Microsoft.Storage/storageAccounts/qkc32cvb2qmmwsawinvm"
60 | },
61 | {
62 | "id": "/subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/armtesting/providers/Microsoft.Network/publicIPAddresses/myPublicIP"
63 | },
64 | {
65 | "id": "/subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/armtesting/providers/Microsoft.Network/virtualNetworks/MyVNET"
66 | },
67 | {
68 | "id": "/subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/armtesting/providers/Microsoft.Network/networkInterfaces/myVMNic"
69 | },
70 | {
71 | "id": "/subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/armtesting/providers/Microsoft.Compute/virtualMachines/SimpleWinVM"
72 | }
73 | ]
74 | }
75 |
--------------------------------------------------------------------------------
/Tests/MockObjects/azuredeploy.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
3 | "contentVersion": "1.0.0.0",
4 | "parameters": {
5 | "storageAccountPrefix": {
6 | "type": "string",
7 | "maxLength": 11,
8 | "defaultValue": "armsta",
9 | "metadata": {
10 | "description": "Prefix the storageaccount"
11 | }
12 | },
13 | "storageAccountType": {
14 | "type": "string",
15 | "defaultValue": "Standard_LRS",
16 | "allowedValues": [
17 | "Standard_LRS",
18 | "Premium_LRS",
19 | "Standard_RAGRS"
20 | ],
21 | "metadata": {
22 | "description": "The type for the storage account"
23 | }
24 | }
25 | },
26 | "variables": {},
27 | "resources": [],
28 | "outputs": {}
29 |
30 | }
--------------------------------------------------------------------------------
/Tests/PSScriptAnalyzer.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | .SYNOPSIS
3 | Run PSScriptAnalyzertests against all scripts
4 | .DESCRIPTION
5 | This scripts runs check with PSScriptanalyzer.
6 | .PARAMETER ScriptPath
7 | The Path where the scripts are. This could be the whole repository or a specific Scriptfolder.
8 | .PARAMETER Exclusions
9 | Define values that don't need to be checked, because they are and accepted risk. For example: PSAvoidUsingUserNameAndPassWordParams if using a lab environment
10 | .PARAMETER Manual
11 | If used, more tests will be ran. These tests check best practices, but aren't needed to run the script through the build.
12 | .EXAMPLE
13 | .\PSScriptAnalyzer.ps1 -ScriptPath C:\Scripts\ExampleRepo -Exclusions PSAvoidUsingUserNameAndPassWordParams -Manual
14 | Script will run manually and provide all testresults. Rule PSAvoidUsingUserNameAndPassWordParams will be ignored
15 | .NOTES
16 | This script is written to use in a build pipeline, but can be ran manually without issues.
17 | Written by Barbara Forbes
18 | #>
19 |
20 | Param(
21 | [Parameter(Mandatory = $true)]
22 | [String]$ScriptPath,
23 | [Parameter()]
24 | [String[]]$Exclusions,
25 | # Parameter help description
26 | [Parameter()]
27 | [Switch]$Manual
28 | )
29 |
30 | $Scripts = Get-ChildItem $ScriptPath -Include *.ps1, *.psm1, *.psd1 -Recurse
31 | $ErrorFound = $false
32 | foreach ($Script in $Scripts) {
33 | Write-Output "Checking $($Script.FullName)"
34 | $PSScriptAnalyzer = Invoke-ScriptAnalyzer -Path $Script.fullname
35 | if ($Manual) {
36 | $PSScriptAnalyzer
37 | continue
38 | }
39 |
40 | Foreach ($Result in $PSScriptAnalyzer) {
41 | if (($Result.Severity -eq "Error") -and ($Exclusions -notcontains $Result.RuleName) ) {
42 | Write-Output "Rule triggered:"
43 | Write-Output $Result
44 | $ErrorFound = $true
45 | }
46 | }
47 | }
48 |
49 | if ($ErrorFound -eq $true) {
50 | Exit 1
51 | }
52 | else {
53 | Write-Output "No errors found"
54 | }
55 |
--------------------------------------------------------------------------------
/Tests/Test-ARMAzureModule.tests.ps1:
--------------------------------------------------------------------------------
1 | $projectRoot = Resolve-Path "$PSScriptRoot\.."
2 | $moduleRoot = Split-Path (Resolve-Path "$projectRoot\*\*.psm1")
3 | $moduleName = Split-Path $moduleRoot -Leaf
4 |
5 | Import-Module (Join-Path $moduleRoot "$moduleName.psd1") -force
6 |
7 |
8 | Describe 'Test-ARMAzureModule' -Tag @("Mock") {
9 | InModuleScope ARMHelper {
10 | Context 'When module is not loaded' {
11 | It "If none are available, script should throw" {
12 | Mock Get-InstalledModule -MockWith { $null }
13 | Mock Get-Module -MockWith { $null }
14 | { Test-ARMAzureModule } | Should -Throw "neither AZ of AzureRM could be loaded"
15 |
16 | }
17 | function Get-AzContext ([String]$Name, [Object]$Value, [Switch]$Clobber) { }
18 | function Get-AzureRMContext([String]$Name, [Object]$Value, [Switch]$Clobber) { }
19 | Mock Get-Module { $null }
20 | Mock Import-Module { $null }
21 | Mock Get-InstalledModule -MockWith {
22 | [pscustomobject]@{
23 | "Version" = "6.13.1"
24 | "Name" = "AzureRM"
25 | "Repository" = "PSGallery"
26 | "Description" = "Azure Resource Manager Module"
27 | }
28 | } -ParameterFilter { $Name -eq "AzureRM" }
29 | Mock Get-InstalledModule -MockWith {
30 | $null
31 | } -ParameterFilter { $Name -eq "Az" }
32 | Mock Get-AzureRmContext -MockWith { "no error" }
33 | Mock Get-AzContext -MockWith { "no error" }
34 | It "If Only AzureRM is available, output should be AzureRM" {
35 | $Result = Test-ARMAzureModule
36 | $Result | Should -be "AzureRM"
37 | }
38 | It "If Only Az is available, output should be Az" {
39 | Mock Get-InstalledModule -MockWith {
40 | $null
41 | } -ParameterFilter { $Name -eq "AzureRM" }
42 | Mock Get-InstalledModule -MockWith {
43 | [pscustomobject]@{
44 | "Version" = "2.4.0"
45 | "Name" = "Az"
46 | "Repository" = "PSGallery"
47 | "Description" = "Azure Resource Manager Module"
48 | }
49 | } -ParameterFilter { $Name -eq "Az" }
50 | Mock Import-Module -MockWith { "no error" }
51 | Mock Get-AzContext -MockWith { "no error" }
52 |
53 | $Result = Test-ARMAzureModule
54 | $Result | Should -be "Az"
55 | }
56 | It "If Both are available, output should be Az" {
57 | Mock Get-InstalledModule -MockWith {
58 | [pscustomobject]@{
59 | "Version" = "6.13.1"
60 | "Name" = "AzureRM"
61 | "Repository" = "PSGallery"
62 | "Description" = "Azure Resource Manager Module"
63 | }
64 | } -ParameterFilter { $Name -eq "AzureRM" }
65 | Mock Get-InstalledModule -MockWith {
66 | [pscustomobject]@{
67 | "Version" = "2.4.0"
68 | "Name" = "Az"
69 | "Repository" = "PSGallery"
70 | "Description" = "Azure Resource Manager Module"
71 | }
72 | } -ParameterFilter { $Name -eq "Az" }
73 | $Result = Test-ARMAzureModule
74 | $Result | Should -be "Az"
75 | }
76 | it "Fails when no connectoin is found"{
77 | Mock Get-AzContext { Throw "error"}
78 | { Test-ARMAzureModule } | Should throw "no connection"
79 | }
80 | }
81 | Context 'When module is loaded' {
82 | function Get-AzContext ([String]$Name, [Object]$Value, [Switch]$Clobber) { }
83 | function Get-AzureRMContext([String]$Name, [Object]$Value, [Switch]$Clobber) { }
84 |
85 | Mock Get-AzureRmContext -MockWith { "no error" }
86 | It "If AzureRM is already loaded, output should be AzureRM" {
87 | Mock Get-InstalledModule -MockWith {
88 | $null
89 | }
90 | Mock Get-Module -MockWith {
91 | $null
92 | } -ParameterFilter { $Name -eq "Az.*" }
93 | Mock Get-Module -MockWith {
94 | [array]@(
95 | "module1",
96 | "Module2"
97 | )
98 | } -ParameterFilter { $Name -eq "AzureRM.*" }
99 | $Result = Test-ARMAzureModule
100 | $Result | Should -be "AzureRM"
101 | }
102 | It "If Az is already loaded, output should be Az" {
103 | Mock Get-InstalledModule -MockWith {
104 | $null
105 | }
106 | Mock Get-Module -MockWith {
107 | $null
108 | } -ParameterFilter { $Name -eq "AzureRM.*" }
109 | Mock Get-Module -MockWith {
110 | [array]@(
111 | "module1",
112 | "Module2"
113 | )
114 | } -ParameterFilter { $Name -eq "Az.*" }
115 |
116 | Mock Get-AzContext -MockWith { "no error" }
117 |
118 | $Result = Test-ARMAzureModule
119 | $Result | Should -be "Az"
120 | }
121 |
122 | }
123 | }
124 | }
--------------------------------------------------------------------------------
/Tests/Test-ARMDeploymentResource.tests.ps1:
--------------------------------------------------------------------------------
1 | $projectRoot = Resolve-Path "$PSScriptRoot\.."
2 | $moduleRoot = Split-Path (Resolve-Path "$projectRoot\*\*.psm1")
3 | $moduleName = Split-Path $moduleRoot -Leaf
4 |
5 | Import-Module (Join-Path $moduleRoot "$moduleName.psd1") -force
6 |
7 |
8 | Describe 'Check Test-ARMDeploymentResource without Azure' -Tag @("Mock") {
9 | InModuleScope ARMHelper {
10 | $Parameters = @{
11 | resourcegroupname = "Arm"
12 | templatefile = "$PSScriptRoot\MockObjects\azuredeploy.json"
13 | templateparameterfile = ".\azuredeploy.parameters.json"
14 | }
15 | Context 'AZ Basic functionality' {
16 | Mock Test-ARMAzureModule { "Az" }
17 | $Mockobject = (Get-Content "$PSScriptRoot\MockObjects\Resultaz.json") | ConvertFrom-Json
18 | Mock Get-ARMResource {
19 | [object]$Mockobject
20 | }
21 | It "Works with a parameterFile" {
22 | { Test-ARMDeploymentResource @Parameters -WarningAction SilentlyContinue } | Should -Not -Throw
23 | }
24 | It "works with a parameter object" {
25 | $Parameterobject = @{
26 | storageAccountPrefix = "armsta"
27 | storageAccountType = "LRS"
28 | }
29 | $Parameters = @{
30 | resourcegroupname = "Arm"
31 | templatefile = "$PSScriptRoot\MockObjects\azuredeploy.json"
32 | templateparameterobject = $Parameterobject
33 | }
34 | { Test-ARMDeploymentResource @Parameters -WarningAction SilentlyContinue } | Should -Not -Throw
35 |
36 | }
37 | It "works with added Parameters" {
38 | $Parameters = @{
39 | resourcegroupname = "Arm"
40 | templatefile = "$PSScriptRoot\MockObjects\azuredeploy.json"
41 | storageAccountPrefix = "armsta"
42 | storageAccountType = "LRS"
43 | }
44 | { Test-ARMDeploymentResource @Parameters -WarningAction SilentlyContinue } | Should -Not -Throw
45 | }
46 |
47 | It "When a deployment is correct, script doesn't throw" {
48 | { Test-ARMDeploymentResource @Parameters -WarningAction SilentlyContinue } | Should -Not -Throw
49 | }
50 | it "Gives a warning for using Az-module"{
51 | $Parameters = @{
52 | resourcegroupname = "Arm"
53 | templatefile = "$PSScriptRoot\MockObjects\azuredeploy.json"
54 | storageAccountPrefix = "armsta"
55 | storageAccountType = "LRS"
56 | }
57 | $Result = (Test-ARMDeploymentResource @Parameters 3>&1)
58 | $Result.Message[0] | Should -BeLike "The AZ-module is used. This limits the results of this cmdlet*"
59 |
60 | }
61 | It "Shows standard properties for resources that would be in the deployment" {
62 | $Result = Test-ARMDeploymentResource @Parameters -WarningAction SilentlyContinue
63 | $Result[0].Resource | Should -be "StorageAccounts"
64 | $Result[0].Name | Should -be "qkc32cvb2qmmwsawinvm"
65 | $Result[0].Type | Should -be "Microsoft.Storage/storageAccounts"
66 | $Result[0].ID | Should -be "/subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/armtesting/providers/Microsoft.Storage/storageAccounts/qkc32cvb2qmmwsawinvm"
67 | }
68 | it "Should throw when no result is found" {
69 | Mock Get-ARMResource { $null }
70 | { Test-ARMDeploymentResource @Parameters } | Should throw "Something is wrong with the output, no resources found. Please check your deployment with Get-ARMdeploymentErrorMessage"
71 | }
72 | It "All Mocks are called" {
73 | Assert-MockCalled -CommandName Get-ARMResource
74 | }
75 | }
76 | Context 'AzureRM Basic functionality' {
77 | Mock Test-ARMAzureModule { "AzureRM" }
78 | $Mockobject = (Get-Content "$PSScriptRoot\MockObjects\Result.json") | ConvertFrom-Json
79 | Mock Get-ARMResource {
80 | [object]$Mockobject
81 | }
82 |
83 | It "Works with a parameterFile" {
84 | { Test-ARMDeploymentResource @Parameters } | Should -Not -Throw
85 | }
86 | It "works with a parameter object" {
87 | $Parameterobject = @{
88 | storageAccountPrefix = "armsta"
89 | storageAccountType = "LRS"
90 | }
91 | $Parameters = @{
92 | resourcegroupname = "Arm"
93 | templatefile = "$PSScriptRoot\MockObjects\azuredeploy.json"
94 | templateparameterobject = $Parameterobject
95 | }
96 | { Test-ARMDeploymentResource @Parameters } | Should -Not -Throw
97 |
98 | }
99 | It "works with added Parameters" {
100 | $Parameters = @{
101 | resourcegroupname = "Arm"
102 | templatefile = "$PSScriptRoot\MockObjects\azuredeploy.json"
103 | storageAccountPrefix = "armsta"
104 | storageAccountType = "LRS"
105 | }
106 | { Test-ARMDeploymentResource @Parameters } | Should -Not -Throw
107 | }
108 |
109 | It "When a deployment is correct, script doesn't throw" {
110 | { Test-ARMDeploymentResource @Parameters } | Should -Not -Throw
111 | }
112 | It "Shows standard properties for resources that would be in the deployment" {
113 | $Result = Test-ARMDeploymentResource @Parameters
114 | $Result[0].Resource | Should -be "StorageAccounts"
115 | $Result[0].Name | Should -be "qkc32cvb2qmmwsawinvm"
116 | $Result[0].Type | Should -be "Microsoft.Storage/storageAccounts"
117 | $Result[0].Location | Should -be "westeurope"
118 | $Result[0].mode | Should -be "Incremental"
119 | $Result[0].ID | Should -be "/subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/armtesting/providers/Microsoft.Storage/storageAccounts/qkc32cvb2qmmwsawinvm"
120 | $Result[0].kind | Should -be "Storage"
121 | $Result[0].sku.name | Should -BeNullOrEmpty
122 | }
123 | It "Shows all properties when using Select-Object *" {
124 | $Result = Test-ARMDeploymentResource @Parameters | Select-Object *
125 | $result[0].'.sku.name' | Should -be "Standard_LRS"
126 | $Result[0].apiVersion | Should -be "2018-11-01"
127 | }
128 | it "When a parameter is a securestring, it is shown as a securestring in the output" {
129 | $Result = Test-ARMDeploymentResource @Parameters
130 | $Result[-1].adminPassword | Should -Be "System.Security.SecureString"
131 | }
132 | it "Should throw when no result is found" {
133 | Mock Get-ARMResource { $null }
134 | { Test-ARMDeploymentResource @Parameters } | Should throw "Something is wrong with the output, no resources found. Please check your deployment with Get-ARMdeploymentErrorMessage"
135 | }
136 | It "All Mocks are called" {
137 | Assert-MockCalled -CommandName Get-ARMResource
138 | }
139 | }
140 | }
141 | }
--------------------------------------------------------------------------------
/Tests/Test-ARMExistingResource.tests.ps1:
--------------------------------------------------------------------------------
1 | $projectRoot = Resolve-Path "$PSScriptRoot\.."
2 | $moduleRoot = Split-Path (Resolve-Path "$projectRoot\*\*.psm1")
3 | $moduleName = Split-Path $moduleRoot -Leaf
4 |
5 | Import-Module (Join-Path $moduleRoot "$moduleName.psd1") -force
6 |
7 |
8 | Describe 'Check Test-ARMExistingResource without Azure' -Tag @("Mock") {
9 |
10 | InModuleScope ARMHelper {
11 | Context 'AZ Incremental' {
12 | function Get-AzResource([String]$Name, [Object]$Value, [Switch]$Clobber) { }
13 | $Parameters = @{
14 | resourcegroupname = "Arm"
15 | templatefile = "$PSScriptRoot\MockObjects\azuredeploy.json"
16 | templateparameterfile = ".\azuredeploy.parameters.json"
17 | Mode = "Incremental"
18 | }
19 | Mock Test-ARMAzureModule { "Az" }
20 | $Mockobject = (Get-Content "$PSScriptRoot\MockObjects\Resultaz.json") | ConvertFrom-Json
21 | Mock Get-ARMResource {
22 | [object]$Mockobject
23 | }
24 | It "When all resources are new, output shows they will be created" {
25 | Mock Get-AzResource { $null }
26 | $Result = Test-ARMExistingResource @Parameters
27 | $Result[0] | Should -Be "Mode for deployment is Incremental `n"
28 | $Result[1] | Should -Be "The following resources do not exist and will be created:"
29 | $Result[2].Type | Should -Be "Microsoft.Storage/storageAccounts"
30 | $Result[2].ResourceGroupName | Should -Be "Arm"
31 | }
32 | It "When resources already exist and mode is incremental, they will be shown as so" {
33 | $MockAZResource = (Get-Content "$PSScriptRoot\MockObjects\ExistingResources.json") | ConvertFrom-Json
34 | Mock Get-AzResource {
35 | [object]$MockAZResource
36 | }
37 | $Result = Test-ARMExistingResource @Parameters
38 | $Result[0] | Should -Be "Mode for deployment is Incremental `n"
39 | $Result[1] | Should -Be "The following resources exist. Mode is set to incremental. New properties might be added:"
40 | $Result[2].Type | Should -Be "Microsoft.Storage/storageAccounts"
41 | $Result[2].ResourceGroupName | Should -Be "Arm"
42 | }
43 | it "When a Microsoft.Deploy is used, a warning is given."{
44 | $Mockobject = (Get-Content "$PSScriptRoot\MockObjects\NestedResult.json") | ConvertFrom-Json
45 | Mock Get-ARMResource {
46 | [object]$Mockobject
47 | }
48 | $Result = (Test-ARMExistingResource @Parameters 3>&1)
49 | $Result.Message[0] | Should -Be "This command does not work for the resourcetype Microsoft.Resources/deployments. Please check linkedTemplate manually."
50 |
51 | }
52 | }
53 | Context 'Az Complete ' {
54 | function Get-AzResource([String]$Name, [Object]$Value, [Switch]$Clobber) { }
55 | $Parameters = @{
56 | resourcegroupname = "Arm"
57 | templatefile = "$PSScriptRoot\MockObjects\azuredeploy.json"
58 | templateparameterfile = ".\azuredeploy.parameters.json"
59 | Mode = "Complete"
60 | }
61 | $Mockobject = (Get-Content "$PSScriptRoot\MockObjects\ResultCompleteaz.json") | ConvertFrom-Json
62 | Mock Get-ARMResource {
63 | [object]$Mockobject
64 | }
65 | Mock Test-ARMAzureModule { "Az" }
66 | It "When resources would be overwritten, they are shown as overwritten" {
67 | $MockAZResource = (Get-Content "$PSScriptRoot\MockObjects\ExistingResources.json") | ConvertFrom-Json
68 | Mock Get-AzResource {
69 | [object]$MockAZResource
70 | }
71 | $Result = Test-ARMExistingResource @Parameters
72 | $Result[0] | Should -Be "Mode for deployment is Complete `n"
73 | $Result[1] | Should -Be "THE FOLLOWING RESOURCES WILL BE OVERWRITTEN! `n Resources exist and mode is complete:"
74 | $Result[2].Type | Should -Be "Microsoft.Storage/storageAccounts"
75 | $Result[2].ResourceGroupName | Should -Be "Arm"
76 | }
77 | it "When resources would be deleted, they are shown as deleted" {
78 | $MockAZResource = (Get-Content "$PSScriptRoot\MockObjects\ExistingResourcesDeleted.json") | ConvertFrom-Json
79 | Mock Get-AzResource {
80 | [object]$MockAZResource
81 | }
82 | $Result = Test-ARMExistingResource @Parameters
83 | $Result[0] | Should -Be "Mode for deployment is Complete `n"
84 | $Result[8] | Should -Be "THE FOLLOWING RESOURCES WILL BE DELETED! `n Resources exist in the resourcegroup but not in the template, mode is complete:"
85 | $Result[9].Type | Should -Be "Microsoft.Storage/storageAccounts"
86 | $Result[9].ResourceGroupName | Should -Be "Arm"
87 | }
88 | it "When ThrowWhenRemoving is used, it will throw if resources are deleted" {
89 | $MockAZResource = (Get-Content "$PSScriptRoot\MockObjects\ExistingResourcesDeleted.json") | ConvertFrom-Json
90 | Mock Get-AzResource {
91 | [object]$MockAZResource
92 | }
93 | { Test-ARMExistingResource @Parameters -ThrowWhenRemoving } | Should throw
94 | }
95 | it "When ThrowWhenRemoving is used, but nothing would be overwritten, it will not throw" {
96 | $MockAZResource = (Get-Content "$PSScriptRoot\MockObjects\ExistingResources.json") | ConvertFrom-Json
97 | Mock Get-AzResource {
98 | [object]$MockAZResource
99 | }
100 | { Test-ARMExistingResource @Parameters -ThrowWhenRemoving } | Should throw
101 | }
102 | it "Should throw when no result is found" {
103 | Mock Get-ARMResource { $null }
104 | { Test-ARMExistingResource @Parameters } | Should throw "Something is wrong with the output, no resources found. Please check your deployment with Get-ARMdeploymentErrorMessage"
105 | }
106 | It "Mocks are called" {
107 | Assert-MockCalled -CommandName Get-ARMResource
108 | Assert-MockCalled -CommandName Get-AZResource
109 | }
110 | }
111 |
112 |
113 | Context 'AzureRM Incremental' {
114 | function Get-AzureRMResource([String]$Name, [Object]$Value, [Switch]$Clobber) { }
115 | $Parameters = @{
116 | resourcegroupname = "Arm"
117 | templatefile = "$PSScriptRoot\MockObjects\azuredeploy.json"
118 | templateparameterfile = ".\azuredeploy.parameters.json"
119 | Mode = "Incremental"
120 | }
121 | Mock Test-ARMAzureModule { "AzureRM" }
122 | $Mockobject = (Get-Content "$PSScriptRoot\MockObjects\Result.json") | ConvertFrom-Json
123 | Mock Get-ARMResource {
124 | [object]$Mockobject
125 | }
126 | It "When all resources are new, output shows they will be created" {
127 |
128 | Mock Get-AzureRMResource { $null }
129 | $Result = Test-ARMExistingResource @Parameters
130 | $Result[0] | Should -Be "Mode for deployment is Incremental `n"
131 | $Result[1] | Should -Be "The following resources do not exist and will be created:"
132 | $Result[2].Type | Should -Be "Microsoft.Storage/storageAccounts"
133 | $Result[2].ResourceGroupName | Should -Be "Arm"
134 | }
135 | It "When resources already exist and mode is incremental, they will be shown as so" {
136 | $MockAZResource = (Get-Content "$PSScriptRoot\MockObjects\ExistingResources.json") | ConvertFrom-Json
137 | Mock Get-AzureRMResource {
138 | [object]$MockAZResource
139 | }
140 | $Result = Test-ARMExistingResource @Parameters
141 | $Result[0] | Should -Be "Mode for deployment is Incremental `n"
142 | $Result[1] | Should -Be "The following resources exist. Mode is set to incremental. New properties might be added:"
143 | $Result[2].Type | Should -Be "Microsoft.Storage/storageAccounts"
144 | $Result[2].ResourceGroupName | Should -Be "Arm"
145 |
146 | }
147 | }
148 | Context 'Complete ' {
149 |
150 | function Get-AzureRMResource([String]$Name, [Object]$Value, [Switch]$Clobber) { }
151 | $Parameters = @{
152 | resourcegroupname = "Arm"
153 | templatefile = "$PSScriptRoot\MockObjects\azuredeploy.json"
154 | templateparameterfile = ".\azuredeploy.parameters.json"
155 | Mode = "Complete"
156 | }
157 | $Mockobject = (Get-Content "$PSScriptRoot\MockObjects\ResultComplete.json") | ConvertFrom-Json
158 | Mock Get-ARMResource {
159 | [object]$Mockobject
160 | }
161 | Mock Test-ARMAzureModule { "AzureRM" }
162 | It "When resources would be overwritten, they are shown as overwritten" {
163 | $MockAZResource = (Get-Content "$PSScriptRoot\MockObjects\ExistingResources.json") | ConvertFrom-Json
164 | Mock Get-AzureRMResource {
165 | [object]$MockAZResource
166 | }
167 | $Result = Test-ARMExistingResource @Parameters
168 | $Result[0] | Should -Be "Mode for deployment is Complete `n"
169 | $Result[1] | Should -Be "THE FOLLOWING RESOURCES WILL BE OVERWRITTEN! `n Resources exist and mode is complete:"
170 | $Result[2].Type | Should -Be "Microsoft.Storage/storageAccounts"
171 | $Result[2].ResourceGroupName | Should -Be "Arm"
172 | }
173 | it "When resources would be deleted, they are shown as deleted" {
174 | $MockAZResource = (Get-Content "$PSScriptRoot\MockObjects\ExistingResourcesDeleted.json") | ConvertFrom-Json
175 | Mock Get-AzureRMResource {
176 | [object]$MockAZResource
177 | }
178 | $Result = Test-ARMExistingResource @Parameters
179 | $Result[0] | Should -Be "Mode for deployment is Complete `n"
180 | $Result[8] | Should -Be "THE FOLLOWING RESOURCES WILL BE DELETED! `n Resources exist in the resourcegroup but not in the template, mode is complete:"
181 | $Result[9].Type | Should -Be "Microsoft.Storage/storageAccounts"
182 | $Result[9].ResourceGroupName | Should -Be "Arm"
183 | }
184 | it "When ThrowWhenRemoving is used, it will throw if resources are deleted" {
185 | $MockAZResource = (Get-Content "$PSScriptRoot\MockObjects\ExistingResourcesDeleted.json") | ConvertFrom-Json
186 | Mock Get-AzureRMResource {
187 | [object]$MockAZResource
188 | }
189 | { Test-ARMExistingResource @Parameters -ThrowWhenRemoving } | Should throw
190 | }
191 | it "When ThrowWhenRemoving is used, but nothing would be overwritten, it will not throw" {
192 | $MockAZResource = (Get-Content "$PSScriptRoot\MockObjects\ExistingResources.json") | ConvertFrom-Json
193 | Mock Get-AzureRMResource {
194 | [object]$MockAZResource
195 | }
196 | { Test-ARMExistingResource @Parameters -ThrowWhenRemoving } | Should throw
197 | }
198 | it "Should throw when no result is found" {
199 | Mock Get-ARMResource { $null }
200 | { Test-ARMExistingResource @Parameters } | Should throw "Something is wrong with the output, no resources found. Please check your deployment with Get-ARMdeploymentErrorMessage"
201 | }
202 | It "Mocks are called" {
203 | Assert-MockCalled -CommandName Get-ARMResource
204 | Assert-MockCalled -CommandName Get-AzureRMResource
205 | }
206 | }
207 | }
208 | }
209 |
--------------------------------------------------------------------------------
/azure-pipelines.yml:
--------------------------------------------------------------------------------
1 | name: Production
2 | trigger:
3 | branches:
4 | include:
5 | - 'master'
6 |
7 |
8 | pr:
9 | - master
10 |
11 | stages:
12 | - stage: Build
13 | jobs:
14 | - job: BuildWindows
15 | pool:
16 | vmImage: windows-2019
17 | steps:
18 | - template: azure-pipelinestemplate.yml
19 | parameters:
20 | platform: 'Windows'
21 | - template: azure-pipelinesazuretemplate.yml
22 | - job: BuildMacOS
23 | pool:
24 | vmImage: macOS-10.14
25 | steps:
26 | - template: azure-pipelinestemplate.yml
27 | parameters:
28 | platform: 'MacOS'
29 | - job: BuildLinux
30 | pool:
31 | vmImage: ubuntu-16.04
32 | steps:
33 | - template: azure-pipelinestemplate.yml
34 | parameters:
35 | platform: 'Linux'
36 |
37 | - stage: Deploy
38 | condition:
39 | and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/master'))
40 | jobs:
41 | - job: DeploytoGallery
42 | pool:
43 | vmImage: windows-2019
44 | steps:
45 | - powershell: |
46 | Publish-Module -Path "$(Build.SourcesDirectory)/ARMHelper" -NuGetApiKey "$(GalleryApiKey)"
47 | displayName: 'Deploy to Gallery'
48 |
--------------------------------------------------------------------------------
/azure-pipelinesazuretemplate.yml:
--------------------------------------------------------------------------------
1 | parameters:
2 | platform: ''
3 |
4 | steps:
5 | - task: AzurePowerShell@3
6 | displayName: 'Pester testing with Azure - v3 ${{ parameters.platform }}'
7 | inputs:
8 | azureSubscription: AzureConnection
9 | ScriptType: InlineScript
10 | Inline: |
11 | $outputFile = "$(Build.SourcesDirectory)\TEST-RESULTS.xml"
12 | Invoke-Pester -Script '$(Build.SourcesDirectory)\Tests\AzureTesting\pipelinetest.ps1' -OutputFile $outputFile -OutputFormat NUnitXml -enableExit
13 | FailOnStandardError: false
14 | azurePowerShellVersion: LatestVersion
15 | continueOnError: true
16 |
17 | - task: AzurePowerShell@4
18 | displayName: 'Pester testing with Azure -v4 ${{ parameters.platform }}'
19 | inputs:
20 | azureSubscription: AzureConnection
21 | ScriptType: InlineScript
22 | Inline: |
23 | $outputFile = "$(Build.SourcesDirectory)\TEST-RESULTS.xml"
24 | Invoke-Pester -Script '$(Build.SourcesDirectory)\Tests\AzureTesting\pipelinetest.ps1' -OutputFile $outputFile -OutputFormat NUnitXml -enableExit
25 | FailOnStandardError: false
26 | azurePowerShellVersion: LatestVersion
27 | continueOnError: true
28 |
29 |
30 | - task: PublishTestResults@2
31 | displayName: 'Publish Online Test Results ${{ parameters.platform }}'
32 | inputs:
33 | testRunTitle: 'online Pester Results Azure ${{ parameters.platform }}'
34 | buildPlatform: 'Azure ${{ parameters.platform }}'
35 | testRunner: 'NUnit'
36 | testResultsFiles: './TEST-RESULTS.xml'
37 | failTaskOnFailedTests: true
38 |
--------------------------------------------------------------------------------
/azure-pipelinestemplate.yml:
--------------------------------------------------------------------------------
1 | parameters:
2 | platform: ''
3 |
4 | steps:
5 | - powershell: |
6 | Get-ChildItem $(Build.SourcesDirectory)/ARMHelper
7 | Install-Module -Name Pester -Force -Scope CurrentUser -SkipPublisherCheck
8 | Install-Module -Name PSScriptAnalyzer -Force -Scope CurrentUser
9 | Import-Module "$(Build.SourcesDirectory)/ARMHelper" -Force
10 | displayName: 'Install Pester and import module'
11 | - powershell: |
12 | $outputFile = ".\TEST-RESULTS.xml"
13 | Invoke-Pester -Tag Mock, Module -OutputFile $outputFile -OutputFormat NUnitXml -enableExit
14 | failOnStderr: false
15 | continueOnError: true
16 | displayName: 'Invoke-Pester without Azure'
17 | - task: PublishTestResults@2
18 | displayName: 'Publish Offline Test Results'
19 | inputs:
20 | testRunTitle: "Offline Pester Results ${{ parameters.platform }}"
21 | buildPlatform: ${{ parameters.platform }}
22 | testRunner: 'NUnit'
23 | testResultsFiles: './TEST-RESULTS.xml'
24 | failTaskOnFailedTests: true
25 |
26 |
--------------------------------------------------------------------------------
/docs/Get-ARMDeploymentErrorMessage.md:
--------------------------------------------------------------------------------
1 | ---
2 | external help file: ARMHelper-help.xml
3 | Module Name: ARMHelper
4 | online version:
5 | schema: 2.0.0
6 | ---
7 |
8 | # Get-ARMDeploymentErrorMessage
9 |
10 | ## SYNOPSIS
11 | Tests an azure deployment for errors, Use the azure Logs if a generic message is given.
12 |
13 | ## SYNTAX
14 |
15 | ### __AllParameterSets (Default)
16 | ```
17 | Get-ARMDeploymentErrorMessage [-ResourceGroupName] [-TemplateFile] [-Pipeline]
18 | [-ThrowOnError] []
19 | ```
20 |
21 | ### TemplateParameterFile
22 | ```
23 | Get-ARMDeploymentErrorMessage [-ResourceGroupName] [-TemplateFile]
24 | -TemplateParameterFile [-Pipeline] [-ThrowOnError] []
25 | ```
26 |
27 | ### TemplateParameterObject
28 | ```
29 | Get-ARMDeploymentErrorMessage [-ResourceGroupName] [-TemplateFile]
30 | -TemplateParameterObject [-Pipeline] [-ThrowOnError] []
31 | ```
32 |
33 | ## DESCRIPTION
34 | This function uses Test-AzureRmResourceGroupDeployment or Test-AZResourcegroupDeployment.
35 | There is a specific errormessage that's very generic.
36 | If this is the output, the correct errormessage is retrieved from the Azurelog.
37 |
38 | ## EXAMPLES
39 |
40 | ### EXAMPLE 1
41 | ```
42 | Get-ARMDeploymentErrorMessage -ResourceGroupName ArmTest -TemplateFile .\azuredeploy.json -TemplateParameterFile .\azuredeploy.parameters.json
43 | ```
44 |
45 | --------
46 | the output is a generic error message. The log is searched for a more clear errormessageGeneral Error. Find info below:
47 | ErrorCode: InvalidDomainNameLabel
48 | Errormessage: The domain name label LABexample is invalid. It must conform to the following regular expression: ^\[a-z\]\[a-z0-9-\]{1,61}\[a-z0-9\]$.
49 |
50 | ### EXAMPLE 2
51 | ```
52 | Get-ARMDeploymentErrorMessage Armtesting .\VM01\azuredeploy.json -TemplateParameterObject $Parameters
53 | ```
54 |
55 | --------
56 | deployment is correct
57 |
58 | ## PARAMETERS
59 |
60 | ### -ResourceGroupName
61 | The resourcegroup where the resources would be deployed to.
62 | This resourcegroup needs to exist.
63 |
64 | ```yaml
65 | Type: String
66 | Parameter Sets: (All)
67 | Aliases:
68 |
69 | Required: True
70 | Position: 2
71 | Default value: None
72 | Accept pipeline input: False
73 | Accept wildcard characters: False
74 | ```
75 |
76 | ### -TemplateFile
77 | The path to the templatefile
78 |
79 | ```yaml
80 | Type: String
81 | Parameter Sets: (All)
82 | Aliases:
83 |
84 | Required: True
85 | Position: 3
86 | Default value: None
87 | Accept pipeline input: False
88 | Accept wildcard characters: False
89 | ```
90 |
91 | ### -TemplateParameterFile
92 | The path to the parameterfile, optional
93 |
94 | ```yaml
95 | Type: String
96 | Parameter Sets: TemplateParameterFile
97 | Aliases:
98 |
99 | Required: True
100 | Position: Named
101 | Default value: None
102 | Accept pipeline input: False
103 | Accept wildcard characters: False
104 | ```
105 |
106 | ### -TemplateParameterObject
107 | A Hasbtable with parameters, optional
108 |
109 | ```yaml
110 | Type: Hashtable
111 | Parameter Sets: TemplateParameterObject
112 | Aliases:
113 |
114 | Required: True
115 | Position: Named
116 | Default value: None
117 | Accept pipeline input: False
118 | Accept wildcard characters: False
119 | ```
120 |
121 | ### -Pipeline
122 | Use this parameter if this script is used in a CICDpipeline.
123 | It will make the step fail.
124 | This parameter is replaced by ThrowOnError and will be removed in a later release!
125 |
126 | ```yaml
127 | Type: SwitchParameter
128 | Parameter Sets: (All)
129 | Aliases:
130 |
131 | Required: False
132 | Position: Named
133 | Default value: False
134 | Accept pipeline input: False
135 | Accept wildcard characters: False
136 | ```
137 |
138 | ### -ThrowOnError
139 | This Switch will make the cmdlet throw when the deployment is incorrect.
140 | This can be useful in a pipeline, it will make the task fail.
141 |
142 | ```yaml
143 | Type: SwitchParameter
144 | Parameter Sets: (All)
145 | Aliases:
146 |
147 | Required: False
148 | Position: Named
149 | Default value: False
150 | Accept pipeline input: False
151 | Accept wildcard characters: False
152 | ```
153 |
154 | ### CommonParameters
155 | This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216).
156 |
157 | ## INPUTS
158 |
159 | ## OUTPUTS
160 |
161 | ## NOTES
162 | Dynamic Parameters like in the orginal Test-AzResourcegroupDeployment-cmdlet are supported
163 | Author: Barbara Forbes
164 | Module: ARMHelper
165 | https://4bes.nl
166 | @Ba4bes
167 |
168 | ## RELATED LINKS
169 |
--------------------------------------------------------------------------------
/docs/Test-ARMDeploymentResource.md:
--------------------------------------------------------------------------------
1 | ---
2 | external help file: ARMHelper-help.xml
3 | Module Name: ARMHelper
4 | online version:
5 | schema: 2.0.0
6 | ---
7 |
8 | # Test-ARMDeploymentResource
9 |
10 | ## SYNOPSIS
11 | Gives output that shows all resources that would be deployed by an ARMtemplate
12 |
13 | ## SYNTAX
14 |
15 | ### __AllParameterSets (Default)
16 | ```
17 | Test-ARMDeploymentResource [-ResourceGroupName] [-TemplateFile] [-Mode ]
18 | []
19 | ```
20 |
21 | ### TemplateParameterFile
22 | ```
23 | Test-ARMDeploymentResource [-ResourceGroupName] [-TemplateFile]
24 | -TemplateParameterFile [-Mode ] []
25 | ```
26 |
27 | ### TemplateParameterObject
28 | ```
29 | Test-ARMDeploymentResource [-ResourceGroupName] [-TemplateFile]
30 | -TemplateParameterObject [-Mode ] []
31 | ```
32 |
33 | ## DESCRIPTION
34 | When you enter a ARM template and a parameter file, this function will show what would be deployed
35 | To do this, it used the debug output of Test-AzureRmResourceGroupDeployment or Test-AzResourceGroupDeployment.
36 | A list of all the resources is provided with the most important properties.
37 | Some resources have seperated functions to structure the output.
38 | If no function is available, a generic output will be given.
39 |
40 | ## EXAMPLES
41 |
42 | ### EXAMPLE 1
43 | ```
44 | Test-ARMDeploymentResource -ResourceGroupName Armtest -TemplateFile .\azuredeploy.json -TemplateParameterFile .\azuredeploy.parameters.json
45 | ```
46 |
47 | --------
48 | Resource : storageAccounts
49 | Name : armsta12356
50 | Type : Microsoft.Storage/storageAccounts
51 | Location : westeurope
52 | mode : Incremental
53 | ID : /subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/arm/providers/Microsoft.Storage/storageAccounts/armsta12356
54 |
55 | ### EXAMPLE 2
56 | ```
57 | Test-ARMDeploymentResource armtesting .\azuredeploy.json -TemplateParameterObject $parameters | select *
58 | ```
59 |
60 | --------
61 | Resource : storageAccounts
62 | Name : armsta12356
63 | Type : Microsoft.Storage/storageAccounts
64 | ID : /subscriptions/12345678-abcd-1234-1234-12345678/resourceGroups/armtesting/providers/Microsoft.Storage/storageAccounts/armsta12356
65 | Location : westeurope
66 | Tags: ARMcreated : True
67 | accountType : Standard_LRS
68 | apiVersion : 2015-06-15
69 | Tags: displayName : armsta12356
70 | mode : Incremental
71 |
72 | ## PARAMETERS
73 |
74 | ### -ResourceGroupName
75 | {{ Fill ResourceGroupName Description }}
76 |
77 | ```yaml
78 | Type: String
79 | Parameter Sets: (All)
80 | Aliases:
81 |
82 | Required: True
83 | Position: 2
84 | Default value: None
85 | Accept pipeline input: False
86 | Accept wildcard characters: False
87 | ```
88 |
89 | ### -TemplateFile
90 | The path to the templatefile
91 |
92 | ```yaml
93 | Type: String
94 | Parameter Sets: (All)
95 | Aliases:
96 |
97 | Required: True
98 | Position: 3
99 | Default value: None
100 | Accept pipeline input: False
101 | Accept wildcard characters: False
102 | ```
103 |
104 | ### -TemplateParameterFile
105 | The path to the parameterfile, optional
106 |
107 | ```yaml
108 | Type: String
109 | Parameter Sets: TemplateParameterFile
110 | Aliases:
111 |
112 | Required: True
113 | Position: Named
114 | Default value: None
115 | Accept pipeline input: False
116 | Accept wildcard characters: False
117 | ```
118 |
119 | ### -TemplateParameterObject
120 | A Hasbtable with parameters, optional
121 |
122 | ```yaml
123 | Type: Hashtable
124 | Parameter Sets: TemplateParameterObject
125 | Aliases:
126 |
127 | Required: True
128 | Position: Named
129 | Default value: None
130 | Accept pipeline input: False
131 | Accept wildcard characters: False
132 | ```
133 |
134 | ### -Mode
135 | {{ Fill Mode Description }}
136 |
137 | ```yaml
138 | Type: String
139 | Parameter Sets: (All)
140 | Aliases:
141 |
142 | Required: False
143 | Position: Named
144 | Default value: Incremental
145 | Accept pipeline input: False
146 | Accept wildcard characters: False
147 | ```
148 |
149 | ### CommonParameters
150 | This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216).
151 |
152 | ## INPUTS
153 |
154 | ## OUTPUTS
155 |
156 | ## NOTES
157 | Dynamic Parameters like in the orginal Test-AzResourcegroupDeployment-cmdlet are supported
158 | Script can be used in a CICD pipeline
159 | Author: Barbara Forbes
160 | Module: ARMHelper
161 | https://4bes.nl
162 | @Ba4bes
163 | Source for more output: #Source https://blog.mexia.com.au/testing-arm-templates-with-pester
164 |
165 | ## RELATED LINKS
166 |
--------------------------------------------------------------------------------
/docs/Test-ARMExistingResource.md:
--------------------------------------------------------------------------------
1 | ---
2 | external help file: ARMHelper-help.xml
3 | Module Name: ARMHelper
4 | online version:
5 | schema: 2.0.0
6 | ---
7 |
8 | # Test-ARMExistingResource
9 |
10 | ## SYNOPSIS
11 | Show if resource that are set to be deployed already exist
12 |
13 | ## SYNTAX
14 |
15 | ### __AllParameterSets (Default)
16 | ```
17 | Test-ARMExistingResource [-ResourceGroupName] [-TemplateFile] [-Mode ]
18 | [-ThrowWhenRemoving] []
19 | ```
20 |
21 | ### TemplateParameterFile
22 | ```
23 | Test-ARMExistingResource [-ResourceGroupName] [-TemplateFile] -TemplateParameterFile
24 | [-Mode ] [-ThrowWhenRemoving] []
25 | ```
26 |
27 | ### TemplateParameterObject
28 | ```
29 | Test-ARMExistingResource [-ResourceGroupName] [-TemplateFile]
30 | -TemplateParameterObject [-Mode ] [-ThrowWhenRemoving] []
31 | ```
32 |
33 | ## DESCRIPTION
34 | This function uses Test-AzureRmResourceGroupDeployment or Test-AzResourceGroupDeployment with debug output to find out what resources are deployed.
35 | After that, it checks if those resources exist in Azure.
36 | It will output the results when using complete mode or incremental mode (depending on the ARM template)
37 |
38 | ## EXAMPLES
39 |
40 | ### EXAMPLE 1
41 | ```
42 | Test-ARMexistingResource -ResourceGroupName ArmTest -TemplateFile .\azuredeploy.json -TemplateParameterFile .\azuredeploy.parameters.json
43 | ```
44 |
45 | --------
46 | The following resources exist. Mode is set to incremental. New properties might be added:
47 |
48 | type name Current ResourcegroupName
49 | ---- ---- -------------------------
50 | Microsoft.Storage/storageAccounts armsta armtest
51 |
52 | ## PARAMETERS
53 |
54 | ### -ResourceGroupName
55 | The resourcegroup where the resources would be deployed to.
56 | This resourcegroup needs to exist.
57 |
58 | ```yaml
59 | Type: String
60 | Parameter Sets: (All)
61 | Aliases:
62 |
63 | Required: True
64 | Position: 2
65 | Default value: None
66 | Accept pipeline input: False
67 | Accept wildcard characters: False
68 | ```
69 |
70 | ### -TemplateFile
71 | The path to the deploymentfile
72 |
73 | ```yaml
74 | Type: String
75 | Parameter Sets: (All)
76 | Aliases:
77 |
78 | Required: True
79 | Position: 3
80 | Default value: None
81 | Accept pipeline input: False
82 | Accept wildcard characters: False
83 | ```
84 |
85 | ### -TemplateParameterFile
86 | The path to the parameterfile
87 |
88 | ```yaml
89 | Type: String
90 | Parameter Sets: TemplateParameterFile
91 | Aliases:
92 |
93 | Required: True
94 | Position: Named
95 | Default value: None
96 | Accept pipeline input: False
97 | Accept wildcard characters: False
98 | ```
99 |
100 | ### -TemplateParameterObject
101 | {{ Fill TemplateParameterObject Description }}
102 |
103 | ```yaml
104 | Type: Hashtable
105 | Parameter Sets: TemplateParameterObject
106 | Aliases:
107 |
108 | Required: True
109 | Position: Named
110 | Default value: None
111 | Accept pipeline input: False
112 | Accept wildcard characters: False
113 | ```
114 |
115 | ### -Mode
116 | The mode in which the deployment will run.
117 | Choose between Incremental or Complete.
118 | Defaults to incremental.
119 |
120 | ```yaml
121 | Type: String
122 | Parameter Sets: (All)
123 | Aliases:
124 |
125 | Required: False
126 | Position: Named
127 | Default value: Incremental
128 | Accept pipeline input: False
129 | Accept wildcard characters: False
130 | ```
131 |
132 | ### -ThrowWhenRemoving
133 | This switch makes the function throw when a resources would be overwritten or deleted.
134 | This can be useful for use in a pipeline.
135 |
136 | ```yaml
137 | Type: SwitchParameter
138 | Parameter Sets: (All)
139 | Aliases:
140 |
141 | Required: False
142 | Position: Named
143 | Default value: False
144 | Accept pipeline input: False
145 | Accept wildcard characters: False
146 | ```
147 |
148 | ### CommonParameters
149 | This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216).
150 |
151 | ## INPUTS
152 |
153 | ## OUTPUTS
154 |
155 | ## NOTES
156 | Dynamic Parameters like in the orginal Test-AzResourcegroupDeployment-cmdlet are supported
157 | Author: Barbara Forbes
158 | Module: ARMHelper
159 | https://4bes.nl
160 | @Ba4bes
161 |
162 | ## RELATED LINKS
163 |
--------------------------------------------------------------------------------