├── publish.ps1 ├── Tests ├── Set-ARMmetadata.Tests.ps1 ├── New-DynamicParam.Tests.ps1 ├── New-ARMfunction.Tests.ps1 ├── Get-ARMresourceList.Tests.ps1 ├── Set-ARMvariable.Tests.ps1 ├── Get-ARMtemplateScript.Tests.ps1 ├── Set-ARMparameter.Tests.ps1 ├── Add-ARMvariable.Tests.ps1 ├── New-ArmTemplate.Tests.ps1 ├── Add-ARMparameter.Tests.ps1 ├── Get-ARMparameter.Tests.ps1 ├── Get-ARMvariable.Tests.ps1 ├── Get-ARMresourceScript.Tests.ps1 ├── Get-ARMvariableScript.Tests.ps1 ├── Get-ARMparameterScript.Tests.ps1 ├── Add-ARMresource.Tests.ps1 ├── Out-HashString.Tests.ps1 ├── New-ARMvariable.Tests.ps1 ├── New-ARMresource.Tests.ps1 ├── Import-ARMtemplate.Tests.ps1 ├── New-ARMparameter.Tests.ps1 ├── Syntax.Tests.ps1 └── ConvertTo-Hash.Tests.ps1 ├── Functions ├── New-ArmTemplate.ps1 ├── Get-ARMtemplate.ps1 ├── Get-ARMvariableScript.ps1 ├── Get-ARMresourceScript.ps1 ├── Get-ARMresourceList.ps1 ├── Get-ARMtemplateScript.ps1 ├── Get-ARMparameterScript.ps1 ├── Set-ARMmetadata.ps1 ├── Add-ARMresource.ps1 ├── New-ARMvariable.ps1 ├── Set-ARMvariable.ps1 ├── Get-ARMvariable.ps1 ├── Add-ARMvariable.ps1 ├── Get-ARMparameter.ps1 ├── Add-ARMparameter.ps1 ├── Import-ARMtemplate.ps1 ├── Set-ARMparameter.ps1 ├── New-ARMparameter.ps1 ├── ConvertTo-Hash.ps1 ├── Update-ARMresourceList.ps1 ├── New-ARMfunction.ps1 ├── New-ARMresource.ps1 ├── Out-HashString.ps1 └── New-DynamicParam.ps1 ├── PoshARM.psm1 ├── LICENSE ├── README.md ├── PoshARM.psd1 └── TestFiles └── SimpleVM.json /publish.ps1: -------------------------------------------------------------------------------- 1 | [cmdletbinding()] 2 | Param( 3 | [Parameter(Mandatory)] 4 | [string] 5 | $APIkey 6 | ) 7 | 8 | $tags = @( 9 | "Azure" 10 | , 11 | "Template" 12 | , 13 | "ARM" 14 | , 15 | "JSON" 16 | , 17 | "Resource" 18 | , 19 | "Manager" 20 | , 21 | "Convert" 22 | , 23 | "Import" 24 | ) 25 | 26 | 27 | Publish-Module -NuGetApiKey $APIkey -Name .\posharm.psd1 -------------------------------------------------------------------------------- /Tests/Set-ARMmetadata.Tests.ps1: -------------------------------------------------------------------------------- 1 | #$here = Split-Path -Parent $MyInvocation.MyCommand.Path 2 | #$sut = (Split-Path -Leaf $MyInvocation.MyCommand.Path) -replace '\.Tests\.', '.' 3 | #. "$here\$sut" 4 | 5 | $modulePath = Split-Path $PSScriptRoot -Parent 6 | $modulepath = Join-Path -Path $modulePath -ChildPath posharm.psd1 7 | Import-Module $modulePath 8 | 9 | Describe "Set-ARMmetadata" { 10 | It "does something useful" { 11 | $true | Should Be $true 12 | } 13 | } 14 | 15 | Remove-Module -name posharm -ErrorAction SilentlyContinue -------------------------------------------------------------------------------- /Tests/New-DynamicParam.Tests.ps1: -------------------------------------------------------------------------------- 1 | #$here = Split-Path -Parent $MyInvocation.MyCommand.Path 2 | #$sut = (Split-Path -Leaf $MyInvocation.MyCommand.Path) -replace '\.Tests\.', '.' 3 | #. "$here\$sut" 4 | 5 | $modulePath = Split-Path $PSScriptRoot -Parent 6 | $modulepath = Join-Path -Path $modulePath -ChildPath posharm.psd1 7 | Import-Module $modulePath 8 | 9 | Describe "New-DynamicParam" { 10 | It "does something useful" { 11 | $true | Should Be $true 12 | } 13 | } 14 | 15 | Remove-Module -name posharm -ErrorAction SilentlyContinue -------------------------------------------------------------------------------- /Tests/New-ARMfunction.Tests.ps1: -------------------------------------------------------------------------------- 1 | #$here = Split-Path -Parent $MyInvocation.MyCommand.Path 2 | #$sut = (Split-Path -Leaf $MyInvocation.MyCommand.Path) -replace '\.Tests\.', '.' 3 | #. "$here\$sut" 4 | 5 | $modulePath = Split-Path $PSScriptRoot -Parent 6 | $modulepath = Join-Path -Path $modulePath -ChildPath posharm.psd1 7 | Import-Module $modulePath 8 | 9 | Describe "New-ARMfunction" { 10 | It "does something useful" { 11 | $true | Should Be $false 12 | } 13 | } 14 | 15 | Remove-Module -name posharm -ErrorAction SilentlyContinue 16 | -------------------------------------------------------------------------------- /Functions/New-ArmTemplate.ps1: -------------------------------------------------------------------------------- 1 | function New-ARMTemplate 2 | { 3 | [cmdletbinding()] 4 | Param( 5 | $schema = "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#" 6 | , 7 | [switch]$Passthru 8 | ) 9 | $templateObject = [pscustomobject][ordered]@{ 10 | '$schema' = $schema 11 | contentVersion = "1.0.0.0" 12 | parameters = [pscustomobject]@{} 13 | variables = [pscustomobject]@{} 14 | resources = @() 15 | outputs = [pscustomobject]@{} 16 | } 17 | 18 | if ($script:Template) 19 | { 20 | $script:Template = $templateObject 21 | } 22 | 23 | if ($Passthru.IsPresent) 24 | { 25 | $templateObject 26 | } 27 | 28 | } -------------------------------------------------------------------------------- /PoshARM.psm1: -------------------------------------------------------------------------------- 1 | $script:Template = [PSCustomObject]@{} 2 | 3 | foreach ($file in (Get-ChildItem -file -Path(Join-Path -Path $PSScriptRoot -ChildPath .\functions))) 4 | { 5 | . ([Scriptblock]::Create([System.IO.File]::ReadAllText($file.FullName, [System.Text.Encoding]::UTF8))) 6 | } 7 | 8 | 9 | 10 | function Get-FunctionList 11 | { 12 | <# 13 | .SYNOPSIS 14 | Internal function 15 | 16 | .DESCRIPTION 17 | Internal function 18 | 19 | .NOTES 20 | Author: Tore Groneng 21 | Website: www.firstpoint.no 22 | Twitter: @ToreGroneng 23 | #> 24 | Param( 25 | [switch] 26 | $AsString 27 | ) 28 | $functions = (Get-Command -Module posharm | Select-Object -Property Name) 29 | $functionsString = ($functions.Name) -join "','" 30 | $functionsString = "'$functionsString'" 31 | 32 | if ($AsString.IsPresent) 33 | { 34 | $functionsString 35 | } 36 | else 37 | { 38 | $functions 39 | } 40 | } -------------------------------------------------------------------------------- /Tests/Get-ARMresourceList.Tests.ps1: -------------------------------------------------------------------------------- 1 | #$here = Split-Path -Parent $MyInvocation.MyCommand.Path 2 | #$sut = (Split-Path -Leaf $MyInvocation.MyCommand.Path) -replace '\.Tests\.', '.' 3 | #. "$here\$sut" 4 | 5 | $modulePath = Split-Path $PSScriptRoot -Parent 6 | $modulepath = Join-Path -Path $modulePath -ChildPath posharm.psd1 7 | Import-Module $modulePath 8 | 9 | Describe "Get-ARMresourceList" { 10 | $resourceList = Get-ARMresourceList 11 | 12 | It "Should return something" { 13 | $resourceList | Should not be $null 14 | } 15 | 16 | It "Should return an array" { 17 | $resourceList -is [array] | should be $true 18 | } 19 | 20 | It "The array should contains strings" { 21 | $resourceList[0].GetType().Name | Should be "String" 22 | } 23 | 24 | Context "Loading list from resourceFile" { 25 | #FIXME add tests here 26 | } 27 | } 28 | 29 | Remove-Module -name posharm -ErrorAction SilentlyContinue -------------------------------------------------------------------------------- /Tests/Set-ARMvariable.Tests.ps1: -------------------------------------------------------------------------------- 1 | #$here = Split-Path -Parent $MyInvocation.MyCommand.Path 2 | #$sut = (Split-Path -Leaf $MyInvocation.MyCommand.Path) -replace '\.Tests\.', '.' 3 | #. "$here\$sut" 4 | 5 | $modulePath = Split-Path $PSScriptRoot -Parent 6 | $modulepath = Join-Path -Path $modulePath -ChildPath posharm.psd1 7 | Import-Module $modulePath 8 | 9 | New-ArmTemplate 10 | 11 | $newVar = New-ARMvariable -Name test -Value testvalue | Add-ARMvariable -PassThru 12 | 13 | $newVariableValue = @{ 14 | Name = "test" 15 | Value = "newTestValue" 16 | } 17 | 18 | Describe "Set-ARMvariable" { 19 | Context "Without pipeline" { 20 | 21 | Set-ARMvariable -Name $newVariableValue.name -Value $newVariableValue.value 22 | $template = Get-ARMtemplate 23 | 24 | It "Should set the value to [$($newVariableValue.value)]" { 25 | $template.variables.test | Should Be $newVariableValue.value 26 | } 27 | } 28 | } 29 | 30 | Remove-Module -name posharm -ErrorAction SilentlyContinue -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Tore Groneng 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 | -------------------------------------------------------------------------------- /Tests/Get-ARMtemplateScript.Tests.ps1: -------------------------------------------------------------------------------- 1 | #$here = Split-Path -Parent $MyInvocation.MyCommand.Path 2 | #$sut = (Split-Path -Leaf $MyInvocation.MyCommand.Path) -replace '\.Tests\.', '.' 3 | #. "$here\$sut" 4 | 5 | $modulePath = Split-Path $PSScriptRoot -Parent 6 | $modulepath = Join-Path -Path $modulePath -ChildPath posharm.psd1 7 | Import-Module $modulePath 8 | 9 | Describe "Get-ARMtemplateScript" { 10 | 11 | Context "Single variable added to template" { 12 | 13 | New-ARMTemplate 14 | $expected = @{ 15 | Name = "test" 16 | Value = "foo-bar" 17 | } 18 | New-ARMvariable @expected | Add-ARMvariable 19 | 20 | $variableScript = Get-ARMtemplate | Get-ARMtemplateScript 21 | $scriptBlock = [scriptblock]::Create($variableScript) 22 | 23 | It "Invoking the script should not throw" { 24 | { $scriptBlock.Invoke() } | Should Not Throw 25 | } 26 | 27 | It "Invoking the script should create a new variable" { 28 | ((Get-ARMtemplate).variables.psobject.properties | Measure-Object).Count | Should be 1 29 | } 30 | } 31 | } 32 | 33 | Remove-Module -name posharm -ErrorAction SilentlyContinue -------------------------------------------------------------------------------- /Tests/Set-ARMparameter.Tests.ps1: -------------------------------------------------------------------------------- 1 | #$here = Split-Path -Parent $MyInvocation.MyCommand.Path 2 | #$sut = (Split-Path -Leaf $MyInvocation.MyCommand.Path) -replace '\.Tests\.', '.' 3 | #. "$here\$sut" 4 | 5 | $modulePath = Split-Path $PSScriptRoot -Parent 6 | $modulepath = Join-Path -Path $modulePath -ChildPath posharm.psd1 7 | Import-Module $modulePath 8 | 9 | New-ArmTemplate 10 | 11 | $newParam = New-ARMparameter -Name test -Type string | Add-ARMparameter -PassThru 12 | 13 | $newParameterValue = @{ 14 | Type = "int" 15 | DefaultValue = "test" 16 | } 17 | 18 | Describe "Set-ARMparameter" { 19 | 20 | Context "Without pipeline" { 21 | 22 | Set-ARMparameter -Name test -Value $newParameterValue 23 | $template = Get-ARMtemplate 24 | 25 | It "Should set the type to [$($newParameterValue.type)]" { 26 | $template.parameters.test.type | Should Be $newParameterValue.type 27 | } 28 | 29 | It "Should set the DefaultValue to [$($newParameterValue.DefaultValue)]" { 30 | $template.parameters.test.defaultvalue | should be $newParameterValue.DefaultValue 31 | } 32 | } 33 | } 34 | 35 | Remove-Module -name posharm -ErrorAction SilentlyContinue -------------------------------------------------------------------------------- /Tests/Add-ARMvariable.Tests.ps1: -------------------------------------------------------------------------------- 1 | #$here = Split-Path -Parent $MyInvocation.MyCommand.Path 2 | #$sut = (Split-Path -Leaf $MyInvocation.MyCommand.Path) -replace '\.Tests\.', '.' 3 | #. "$here\$sut" 4 | 5 | $modulePath = Split-Path $PSScriptRoot -Parent 6 | $modulepath = Join-Path -Path $modulePath -ChildPath posharm.psd1 7 | Import-Module $modulePath 8 | 9 | Describe "Add-ARMvariable" { 10 | New-ArmTemplate 11 | $expected = @{ 12 | Name = "Test" 13 | Value = "foo-bar" 14 | } 15 | 16 | $actual = New-ARMvariable @expected 17 | 18 | Context "Without pipeline" { 19 | Add-ARMvariable -InputObject $actual 20 | $template = Get-ARMTemplate 21 | 22 | It "Should add a variable to a template" { 23 | $template.variables | should not be $null 24 | } 25 | 26 | It "Should have a property named [$($expected.Name)]" { 27 | $template.variables.($expected.Name) | should not be $null 28 | } 29 | } 30 | 31 | Context "With pipeline" { 32 | New-ArmTemplate 33 | 34 | $actual | Add-ARMvariable 35 | $template = Get-ARMTemplate 36 | 37 | It "Should add a variable to a template" { 38 | $template.variables | should not be $null 39 | } 40 | 41 | It "Should have a property named [$($expected.Name)]" { 42 | $template.variables.($expected.Name) | should not be $null 43 | } 44 | } 45 | } 46 | 47 | Remove-Module -name posharm -ErrorAction SilentlyContinue -------------------------------------------------------------------------------- /Functions/Get-ARMtemplate.ps1: -------------------------------------------------------------------------------- 1 | #Requires -Version 5.0 2 | function Get-ARMtemplate 3 | { 4 | <# 5 | .SYNOPSIS 6 | Get the ARM template defined at the module level 7 | 8 | .DESCRIPTION 9 | Get the ARM template defined at the module level 10 | 11 | .PARAMETER AsJSON 12 | Returns the ARM template as a JSON string 13 | 14 | .PARAMETER AsHashTableString 15 | Returns the ARM template as a Hashtable object 16 | 17 | .EXAMPLE 18 | Get-ARMtemplate | Get-ARMtempateScript 19 | 20 | .INPUTS 21 | PSCustomObject 22 | 23 | .OUTPUTS 24 | string 25 | 26 | .NOTES 27 | Author: Tore Groneng 28 | Website: www.firstpoint.no 29 | Twitter: @ToreGroneng 30 | #> 31 | [cmdletbinding( 32 | DefaultParameterSetName='foo' 33 | )] 34 | Param( 35 | [Parameter(ParameterSetName='JSON')] 36 | [switch]$AsJSON 37 | , 38 | [Parameter(ParameterSetName='HASH')] 39 | [switch]$AsHashTableString 40 | ) 41 | 42 | Begin 43 | { 44 | $f = $MyInvocation.InvocationName 45 | Write-Verbose -Message "$f - START" 46 | } 47 | 48 | Process 49 | { 50 | if ($AsJSON.IsPresent -eq $false -and $AsHashTableString.IsPresent -eq $false) 51 | { 52 | $Script:Template 53 | } 54 | 55 | if ($AsJSON.IsPresent) 56 | { 57 | $json = $Script:Template | ConvertTo-Json -Depth 99 58 | $json = $json.replace("\u0027","'")#' 59 | $json 60 | } 61 | 62 | if ($AsHashTableString.IsPresent) 63 | { 64 | $Script:Template | ConvertTo-Hash | Out-HashString 65 | } 66 | } 67 | 68 | } -------------------------------------------------------------------------------- /Functions/Get-ARMvariableScript.ps1: -------------------------------------------------------------------------------- 1 | #Requires -Version 5.0 2 | function Get-ARMvariableScript 3 | { 4 | <# 5 | .SYNOPSIS 6 | Get the Powershell script that will recreate the variables in the ARM template 7 | 8 | .DESCRIPTION 9 | Get the Powershell script that will recreate the variables in the ARM template. This cmdlet is invoked by the Get-ARMtemplateScript cmdlet. 10 | 11 | .PARAMETER Variables 12 | The variables propterty of the ARM template 13 | 14 | .EXAMPLE 15 | Get-ARMtemplate | Select-Object Variables | Get-ARMvariableScript 16 | 17 | .INPUTS 18 | PSCustomObject 19 | 20 | .OUTPUTS 21 | string 22 | 23 | .NOTES 24 | Author: Tore Groneng 25 | Website: www.firstpoint.no 26 | Twitter: @ToreGroneng 27 | #> 28 | [cmdletbinding()] 29 | Param( 30 | [Parameter(ValueFromPipeline)] 31 | [PSCustomObject] 32 | $Variables 33 | ) 34 | 35 | Begin 36 | { 37 | $f = $MyInvocation.InvocationName 38 | Write-Verbose -Message "$f - START" 39 | $cmdLine = "" 40 | } 41 | 42 | Process 43 | { 44 | #if ($Variables.variables) 45 | if ($Variables) 46 | { 47 | $allVars = $Variables | ConvertTo-Hash 48 | 49 | foreach ($key in $allVars.Keys) 50 | { 51 | Write-Verbose -Message "$f - Processing key [$key]" 52 | 53 | $cmdLine += "New-ARMvariable -Name $key -Value " + '"' + $($allVars.$key) + '" | Add-ARMVariable' 54 | $cmdLine += [environment]::NewLine 55 | } 56 | } 57 | } 58 | 59 | End 60 | { 61 | $cmdLine 62 | Write-Verbose -Message "$f - END" 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /Functions/Get-ARMresourceScript.ps1: -------------------------------------------------------------------------------- 1 | #Requires -Version 5.0 2 | function Get-ARMresourceScript 3 | { 4 | <# 5 | .SYNOPSIS 6 | Get the Powershell script that will recreate the resources in the ARM template 7 | 8 | .DESCRIPTION 9 | Get the Powershell script that will recreate the resources in the ARM template. This cmdlet is invoked by the Get-ARMtemplateScript cmdlet. 10 | 11 | .PARAMETER Resources 12 | The resources propterty of the ARM template 13 | 14 | .EXAMPLE 15 | Get-ARMtemplate | Select-Object resources | Get-ARMresourceScript 16 | 17 | .INPUTS 18 | PSCustomObject 19 | 20 | .OUTPUTS 21 | string 22 | 23 | .NOTES 24 | Author: Tore Groneng 25 | Website: www.firstpoint.no 26 | Twitter: @ToreGroneng 27 | #> 28 | [cmdletbinding()] 29 | Param( 30 | [Parameter(ValueFromPipeline)] 31 | [PSCustomObject] 32 | $Resources 33 | ) 34 | 35 | Begin 36 | { 37 | $f = $MyInvocation.InvocationName 38 | Write-Verbose -Message "$f - START" 39 | 40 | $cmd = Get-Command -Name New-ARMresource 41 | $cmdParams = $cmd.Parameters.Keys 42 | } 43 | 44 | Process 45 | { 46 | foreach ($resource in $Resources) 47 | { 48 | $hash = $resource | ConvertTo-Hash 49 | $cmdline = '$resource = ' 50 | 51 | foreach ($key in $hash.Keys) 52 | { 53 | if ($key -notin $cmdParams) 54 | { 55 | Write-Warning -Message "Parameter [$key] not found in $($cmd.Name)" 56 | } 57 | } 58 | 59 | $params = $hash | Out-HashString 60 | $cmdline = "$cmdline$params" + [environment]::NewLine 61 | "$cmdline" + "New-ARMresource @resource | Add-ARMresource" + [environment]::NewLine + [environment]::NewLine 62 | } 63 | } 64 | } -------------------------------------------------------------------------------- /Functions/Get-ARMresourceList.ps1: -------------------------------------------------------------------------------- 1 | #Requires -Version 5.0 2 | function Get-ARMresourceList 3 | { 4 | <# 5 | .SYNOPSIS 6 | Get a list of resource providers in Azure 7 | 8 | .DESCRIPTION 9 | Get a list of resource providers in Azure. The list is read from an cached file in the module (.\Data\allResources.json) 10 | See also Update-ARMresourceList cmdlet. 11 | 12 | .PARAMETER ResourceFile 13 | The path to a file that have the definition of the resource providers. 14 | 15 | .EXAMPLE 16 | Get-ARMresourceList 17 | 18 | .EXAMPLE 19 | Get-ARMresourceList -ResourceFile c:\temp\allResources.json 20 | 21 | .INPUTS 22 | PSCustomObject 23 | 24 | .OUTPUTS 25 | string 26 | 27 | .NOTES 28 | Author: Tore Groneng 29 | Website: www.firstpoint.no 30 | Twitter: @ToreGroneng 31 | #> 32 | [cmdletbinding()] 33 | Param( 34 | [string] 35 | $ResourceFile = (Split-Path -Path $PSScriptRoot -Parent | Join-Path -childpath Data | join-path -childpath allResources.json) 36 | ) 37 | 38 | $f = $MyInvocation.InvocationName 39 | Write-Verbose -Message "$f - START" 40 | 41 | Write-Verbose -Message "$f - Reading content from [$ResourceFile]" 42 | 43 | $jsonContent = Get-Content -Path $ResourceFile -Encoding UTF8 44 | 45 | $allResources = $jsonContent | ConvertFrom-Json 46 | 47 | $list = New-Object -TypeName System.Collections.Generic.List[string] 48 | 49 | foreach ($Resource in $allResources) 50 | { 51 | $providerNameSpace = $Resource.ProviderNamespace 52 | foreach ($type in $Resource.ResourceTypes) 53 | { 54 | $fullNameSpace = "$providerNameSpace/$($type.ResourceTypeName)" 55 | $list.Add($fullNameSpace) 56 | } 57 | } 58 | 59 | $list 60 | 61 | Write-Verbose -Message "$f - END" 62 | } -------------------------------------------------------------------------------- /Tests/New-ArmTemplate.Tests.ps1: -------------------------------------------------------------------------------- 1 | #$here = Split-Path -Parent $MyInvocation.MyCommand.Path 2 | #$sut = (Split-Path -Leaf $MyInvocation.MyCommand.Path) -replace '\.Tests\.', '.' 3 | #. "$here\$sut" 4 | 5 | $modulePath = Split-Path $PSScriptRoot -Parent 6 | $modulepath = Join-Path -Path $modulePath -ChildPath poshARM.psd1 7 | Import-Module $modulePath 8 | 9 | Describe "New-ARMTemplate" { 10 | Context "Create Object" { 11 | $new = New-ArmTemplate 12 | It "Should return nothing if Passthru is not specified" { 13 | $new | Should Be $null 14 | } 15 | 16 | It "Should save the template in the Script:Template variable" { 17 | Get-ARMTemplate | should not be $null 18 | } 19 | 20 | It "Should have a [contentVersion] proptery" { 21 | (Get-ARMTemplate).contentVersion | Should be '1.0.0.0' 22 | } 23 | 24 | It "Should have a [parameters] property of type [PScustomobject]" { 25 | (Get-ARMTemplate).parameters.GetType().Name | Should be 'PScustomobject' 26 | } 27 | 28 | It "Should have a [variables] property of type [PScustomobject]" { 29 | (Get-ARMTemplate).variables.GetType().Name | Should be 'PScustomobject' 30 | } 31 | 32 | It "Should have a [resources] property of type Object[]" { 33 | (Get-ARMTemplate).resources.GetType().Name | Should be 'Object[]' 34 | } 35 | 36 | It "Should have a [outputs] property of type [PScustomobject]" { 37 | (Get-ARMTemplate).outputs.GetType().Name | Should be 'PScustomobject' 38 | } 39 | 40 | It "Should return an object if Passthru is specified" { 41 | $new = New-ArmTemplate -Passthru 42 | $new | Should not Be $null 43 | } 44 | } 45 | } 46 | 47 | Remove-Module -name posharm -ErrorAction SilentlyContinue -------------------------------------------------------------------------------- /Tests/Add-ARMparameter.Tests.ps1: -------------------------------------------------------------------------------- 1 | #$here = Split-Path -Parent $MyInvocation.MyCommand.Path 2 | #$sut = (Split-Path -Leaf $MyInvocation.MyCommand.Path) -replace '\.Tests\.', '.' 3 | #. "$here\$sut" 4 | #Import-Module C:\Users\tore\Dropbox\SourceTreeRepros\PoshARM 5 | 6 | $modulePath = Split-Path $PSScriptRoot -Parent 7 | $modulepath = Join-Path -Path $modulePath -ChildPath posharm.psd1 8 | Import-Module $modulePath 9 | 10 | Describe "Add-ARMparameter" { 11 | 12 | $expected = @{ 13 | Name = 'Resource' 14 | Type = 'string' 15 | DefaultValue = 'meh' 16 | AllowedValues = @("meh", "det", "foo") 17 | MinValue = 1 18 | MaxValue = 3 19 | MinLength = '3' 20 | MaxLength = '99' 21 | Description = 'Description' 22 | Metadata = @{Comment="yalla"} 23 | } 24 | 25 | $actual = New-ARMparameter @expected 26 | New-ArmTemplate 27 | 28 | Context "Without pipeline" { 29 | Add-ARMparameter -InputObject $actual 30 | $template = Get-ARMTemplate 31 | 32 | It "Should add a parameter to a template" { 33 | $template.parameters | should not be $null 34 | } 35 | 36 | It "Should have a property named [$($expected.Name)]" { 37 | $template.parameters.($expected.Name) | should not be $null 38 | } 39 | } 40 | 41 | Context "With pipeline" { 42 | New-ArmTemplate 43 | 44 | $actual | Add-ARMparameter 45 | $template = Get-ARMTemplate 46 | 47 | It "Should add a parameter to a template" { 48 | $template.parameters | should not be $null 49 | } 50 | 51 | It "Should have a property named [$($expected.Name)]" { 52 | $template.parameters.($expected.Name) | should not be $null 53 | } 54 | } 55 | } 56 | 57 | Remove-Module -name posharm -ErrorAction SilentlyContinue -------------------------------------------------------------------------------- /Functions/Get-ARMtemplateScript.ps1: -------------------------------------------------------------------------------- 1 | #Requires -Version 5.0 2 | function Get-ARMtemplateScript 3 | { 4 | <# 5 | .SYNOPSIS 6 | Get the Powershell script that will recreate the ARM template 7 | 8 | .DESCRIPTION 9 | Get the Powershell script that will recreate the ARM template 10 | 11 | .PARAMETER Template 12 | An PScustomObject that is an ARM template 13 | 14 | .EXAMPLE 15 | Get-ARMtemplate | Get-ARMtemplateScript 16 | 17 | .INPUTS 18 | PSCustomObject 19 | 20 | .OUTPUTS 21 | string 22 | 23 | .NOTES 24 | Author: Tore Groneng 25 | Website: www.firstpoint.no 26 | Twitter: @ToreGroneng 27 | #> 28 | [cmdletbinding()] 29 | Param( 30 | [Parameter(ValueFromPipeline)] 31 | [pscustomobject]$Template 32 | ) 33 | 34 | Begin 35 | { 36 | $f = $MyInvocation.InvocationName 37 | Write-Verbose -Message "$f - START" 38 | $stringBuilder = New-Object -TypeName System.Text.StringBuilder 39 | } 40 | 41 | Process 42 | { 43 | if ($Template) 44 | { 45 | $null = $stringBuilder.AppendLine('New-ARMtemplate') 46 | $null = $stringBuilder.AppendLine() 47 | 48 | Write-Verbose -Message "$f - Processing variables" 49 | [string]$vars = $Template.variables | Get-ARMvariableScript 50 | $null = $stringBuilder.AppendLine($vars) 51 | 52 | Write-Verbose -Message "$f - Processing parameters" 53 | [string]$params = $Template.parameters | Get-ARMparameterScript 54 | $null = $stringBuilder.AppendLine($params) 55 | 56 | Write-Verbose -Message "$f - Processing resources" 57 | foreach ($resource in $Template.resources) 58 | { 59 | [string]$res = $resource | Get-ARMresourceScript 60 | $null = $stringBuilder.AppendLine($res) 61 | } 62 | } 63 | } 64 | 65 | End 66 | { 67 | Write-Verbose -Message "$f - END" 68 | $stringBuilder.ToString() 69 | } 70 | 71 | } -------------------------------------------------------------------------------- /Functions/Get-ARMparameterScript.ps1: -------------------------------------------------------------------------------- 1 | #Requires -Version 5.0 2 | function Get-ARMparameterScript 3 | { 4 | <# 5 | .SYNOPSIS 6 | Get the Powershell script that will recreate the parameteres in the ARM template 7 | 8 | .DESCRIPTION 9 | Get the Powershell script that will recreate the parameteres in the ARM template. This cmdlet is invoked by the Get-ARMtemplateScript cmdlet. 10 | 11 | .PARAMETER Parameters 12 | The parameters property of the ARM template 13 | 14 | .EXAMPLE 15 | Get-ARMtemplate | Select-Object parameters | Get-ARMparameterScript 16 | 17 | .INPUTS 18 | PSCustomObject 19 | 20 | .OUTPUTS 21 | string 22 | 23 | .NOTES 24 | Author: Tore Groneng 25 | Website: www.firstpoint.no 26 | Twitter: @ToreGroneng 27 | #> 28 | 29 | [cmdletbinding()] 30 | Param( 31 | [Parameter(ValueFromPipeline)] 32 | [PSCustomObject] 33 | $Parameters 34 | ) 35 | 36 | Begin 37 | { 38 | $f = $MyInvocation.InvocationName 39 | Write-Verbose -Message "$f - START" 40 | } 41 | 42 | Process 43 | { 44 | Write-Verbose -Message "$f - Converting to hashtable" 45 | 46 | $allParams = $Parameters | ConvertTo-Hash 47 | 48 | foreach ($key in $allParams.Keys) 49 | { 50 | Write-Verbose -Message "$f - Processing key [$key]" 51 | 52 | $cmdline = '$parameter = ' 53 | $paramHash = @{ 54 | Name = $key 55 | } 56 | 57 | foreach ($subkey in $allParams.$key.Keys) 58 | { 59 | $paramHash.Add($subkey,$allParams.$key.$subkey) 60 | } 61 | 62 | $params = $paramHash | Out-HashString 63 | 64 | $cmdline = "$cmdline $params" + [environment]::NewLine 65 | "$cmdline" + "New-ARMparameter @parameter | Add-ARMparameter" + [environment]::NewLine + [environment]::NewLine 66 | } 67 | } 68 | 69 | End 70 | { 71 | Write-Verbose -Message "$f - END" 72 | 73 | } 74 | } -------------------------------------------------------------------------------- /Functions/Set-ARMmetadata.ps1: -------------------------------------------------------------------------------- 1 | function Set-ARMmetadata 2 | { 3 | <# 4 | .SYNOPSIS 5 | Creates a metadata.json file for your ARM template 6 | 7 | .DESCRIPTION 8 | Creates a metadata.json file for your ARM template 9 | 10 | .EXAMPLE 11 | $meta = @{ 12 | ItemDisplayName = "Blank Template" 13 | Description = "A blank template and empty parameters file" 14 | Summary = "A blank template and empty parameters file. " 15 | GitHubUsername = "torgro" 16 | } 17 | Set-ARMmetadata @meta 18 | 19 | This will create a file metadata.json containing the information in $meta 20 | 21 | .INPUTS 22 | string 23 | 24 | .OUTPUTS 25 | 26 | .NOTES 27 | Author: Tore Groneng 28 | Website: www.firstpoint.no 29 | Twitter: @ToreGroneng 30 | #> 31 | [cmdletbinding()] 32 | Param( 33 | $ItemDisplayName 34 | , 35 | $Description 36 | , 37 | $Summary 38 | , 39 | $GitHubUsername 40 | , 41 | $FileName = "metadata.json" 42 | ) 43 | $f = $MyInvocation.InvocationName 44 | Write-Verbose -Message "$f - START" 45 | 46 | $resolvedFilename = Resolve-Path -Path $FileName -ErrorAction SilentlyContinue 47 | 48 | if (-not $resolvedFilename) 49 | { 50 | $resolvedFilename = Join-Path -Path $PSScriptRoot -ChildPath metadata.json 51 | } 52 | 53 | $output = [pscustomobject]@{ 54 | itemDisplayName = $ItemDisplayName 55 | description = $Description 56 | summary = $Summary 57 | githubUsername = $GitHubUsername 58 | dateUpdated = (Get-Date).ToString("yyyy-MM-dd") 59 | } 60 | 61 | $json = $output | ConvertTo-Json 62 | Write-Verbose -Message "$f - json output $json" 63 | Write-Verbose -Message "$f - Writing to file [$resolvedFilename]" 64 | 65 | Set-Content -Path $resolvedFilename -Value $json -Encoding UTF8 66 | 67 | Write-Verbose -Message "$f - END" 68 | } -------------------------------------------------------------------------------- /Tests/Get-ARMparameter.Tests.ps1: -------------------------------------------------------------------------------- 1 | #$here = Split-Path -Parent $MyInvocation.MyCommand.Path 2 | #$sut = (Split-Path -Leaf $MyInvocation.MyCommand.Path) -replace '\.Tests\.', '.' 3 | #. "$here\$sut" 4 | #Import-Module "C:\Users\tore\Dropbox\SourceTreeRepros\PoshARM\PoshARM.psd1" 5 | 6 | $modulePath = Split-Path $PSScriptRoot -Parent 7 | $modulepath = Join-Path -Path $modulePath -ChildPath posharm.psd1 8 | Import-Module $modulePath 9 | 10 | Describe "Get-ARMparameter" { 11 | New-ArmTemplate 12 | $expected1 = @{ 13 | Name = "Param1" 14 | Type = "string" 15 | } 16 | 17 | $expected2 = @{ 18 | Name = "Param2" 19 | Type = "int" 20 | } 21 | New-ARMparameter @expected1 | Add-ARMparameter 22 | New-ARMparameter @expected2 | Add-ARMparameter 23 | 24 | $actual1 = Get-ARMparameter -Name param1 25 | $actual2 = Get-ARMparameter -Name param2 26 | 27 | It "Should return all parameters if no name is provided" { 28 | ((Get-ARMparameter).PSobject.Properties | Measure-Object).Count | Should be 2 29 | } 30 | 31 | It "Parameter object 1 should not be `$null" { 32 | $actual1 | Should not be $null 33 | } 34 | 35 | It "Parameter object 1 should have a property named [$($expected1.Name)]" { 36 | $actual1.($expected1.Name) | Should not be $null 37 | } 38 | 39 | It "Parameter object 2 should not be `$null" { 40 | $actual2 | Should not be $null 41 | } 42 | 43 | It "Parameter object 2 should have a property named [$($expected2.Name)]" { 44 | $actual2.($expected2.Name) | Should not be $null 45 | } 46 | 47 | $name = $expected1.Name 48 | $actualObj = $actual1.$name 49 | 50 | It "'$name' should have a property Type [$($expected1.type)]" { 51 | $actualObj.Type | Should be $expected1.Type 52 | } 53 | 54 | $name = $expected2.Name 55 | $actualObj = $actual2.$name 56 | 57 | It "'$name' should have a property Type [$($expected2.type)]" { 58 | $actualObj.Type | Should be $expected2.Type 59 | } 60 | } -------------------------------------------------------------------------------- /Tests/Get-ARMvariable.Tests.ps1: -------------------------------------------------------------------------------- 1 | #$here = Split-Path -Parent $MyInvocation.MyCommand.Path 2 | #$sut = (Split-Path -Leaf $MyInvocation.MyCommand.Path) -replace '\.Tests\.', '.' 3 | #. "$here\$sut" 4 | #Import-Module "C:\Users\tore\Dropbox\SourceTreeRepros\PoshARM\PoshARM.psd1" 5 | 6 | $modulePath = Split-Path $PSScriptRoot -Parent 7 | $modulepath = Join-Path -Path $modulePath -ChildPath posharm.psd1 8 | Import-Module $modulePath 9 | 10 | Describe "Get-ARMvariable" { 11 | New-ArmTemplate 12 | $expected1 = @{ 13 | Name = "Var1" 14 | value = "Foo-bar1" 15 | } 16 | 17 | $expected2 = @{ 18 | Name = "Var2" 19 | Value = "FooBar2" 20 | } 21 | New-ARMvariable @expected1 | Add-ARMvariable 22 | New-ARMvariable @expected2 | Add-ARMvariable 23 | 24 | $actual1 = Get-ARMvariable -Name Var1 25 | $actual2 = Get-ARMvariable -Name Var2 26 | 27 | It "Should return all variables if no name is provided" { 28 | ((Get-ARMvariable).PSobject.Properties | Measure-Object).Count | Should be 2 29 | } 30 | 31 | It "Variable object 1 should not be `$null" { 32 | $actual1 | Should not be $null 33 | } 34 | 35 | It "Variable object 1 should have a property named [$($expected1.Name)]" { 36 | $actual1.($expected1.Name) | Should not be $null 37 | } 38 | 39 | It "Variable object 2 should not be `$null" { 40 | $actual2 | Should not be $null 41 | } 42 | 43 | It "Variable object 2 should have a property named [$($expected2.Name)]" { 44 | $actual2.($expected2.Name) | Should not be $null 45 | } 46 | 47 | $name = $expected1.Name 48 | $actualValue = $actual1.$name 49 | 50 | It "'$name' should have a property value of [$($expected1.value)]" { 51 | $actualValue | Should be $expected1.value 52 | } 53 | 54 | $name = $expected2.Name 55 | $actualValue = $actual2.$name 56 | 57 | It "'$name' should have a property value of [$($expected2.value)]" { 58 | $actualValue | Should be $expected2.Value 59 | } 60 | } -------------------------------------------------------------------------------- /Functions/Add-ARMresource.ps1: -------------------------------------------------------------------------------- 1 | #Requires -Version 5.0 2 | function Add-ARMresource 3 | { 4 | <# 5 | .SYNOPSIS 6 | Add an ARM resource to an ARM template. 7 | 8 | .DESCRIPTION 9 | Add an ARM resource to an ARM template 10 | 11 | .PARAMETER InputObject 12 | The PSCustomObject of type 'ARMresource' to be added to the ARM template. This parameter is mandatory. 13 | 14 | .PARAMETER Template 15 | The ARM template of type 'ARMtemplate' the parameter should be added to. If not specified, it will add the parameter to the module-level template 16 | 17 | .EXAMPLE 18 | $newParam = @{ 19 | APIversion = '2016-03-30' 20 | Name = 'MyVM' 21 | Location = 'EAST-US' 22 | Tags = @{tag=1} 23 | Comments = 'hey' 24 | DependsOn = @("item1","item2") 25 | SKU = @{value="skuvalue"} 26 | Kind = 'storage' 27 | Properties = @{prop1=1} 28 | } 29 | 30 | New-ARMresource @newParam -Type Microsoft.Compute/virtualMachines | Add-ARMresource 31 | 32 | This will add the resource to the module level template 33 | 34 | .INPUTS 35 | PSCustomObject 36 | 37 | .OUTPUTS 38 | PSCustomObject 39 | 40 | .NOTES 41 | Author: Tore Groneng 42 | Website: www.firstpoint.no 43 | Twitter: @ToreGroneng 44 | #> 45 | [cmdletbinding()] 46 | Param( 47 | [Parameter(Mandatory, ValueFromPipeline)] 48 | [PSTypeName('ARMresource')] 49 | $InputObject 50 | , 51 | [PSTypeName('ARMtemplate')] 52 | $Template 53 | , 54 | [switch] 55 | $PassThru 56 | ) 57 | 58 | Begin 59 | { 60 | $f = $MyInvocation.InvocationName 61 | Write-Verbose -Message "$f - START" 62 | } 63 | 64 | Process 65 | { 66 | if (-not $Template) 67 | { 68 | Write-Verbose -Message "$f - Using module level template" 69 | $Template = $script:Template 70 | } 71 | 72 | if ($Template) 73 | { 74 | $Template.resources += $InputObject 75 | } 76 | 77 | if ($PassThru.IsPresent) 78 | { 79 | $InputObject 80 | } 81 | } 82 | 83 | End 84 | { 85 | Write-Verbose -Message "$f - End" 86 | } 87 | } -------------------------------------------------------------------------------- /Functions/New-ARMvariable.ps1: -------------------------------------------------------------------------------- 1 | #Requires -Version 5.0 2 | function New-ARMvariable 3 | { 4 | <# 5 | .SYNOPSIS 6 | Create a new ARM template variable 7 | 8 | .DESCRIPTION 9 | Create a new ARM template variable 10 | 11 | .PARAMETER Name 12 | The name of the variable. This is Mandatory 13 | 14 | .PARAMETER Value 15 | The variable value. This is Mandatory 16 | 17 | .EXAMPLE 18 | New-ARMvariable -Name nicName -Value "myNIC" 19 | 20 | This will create a new variable named nicName of with vavlue "myNIC" 21 | 22 | .EXAMPLE 23 | $name = "subnet" 24 | $expectedHash = @{ 25 | Name = "NameKey" 26 | Ipaddress = "10.0.0.2" 27 | SubNet = "255.255.255.0" 28 | } 29 | $expectedHash | New-ARMvariable -Name $name 30 | 31 | This will create a new variable named subnet which have 3 properties: Name, IpAddress and SubNet 32 | 33 | .INPUTS 34 | String 35 | 36 | .OUTPUTS 37 | PSCustomObject 38 | 39 | .NOTES 40 | Author: Tore Groneng 41 | Website: www.firstpoint.no 42 | Twitter: @ToreGroneng 43 | #> 44 | [cmdletbinding()] 45 | Param( 46 | [Parameter(Mandatory)] 47 | [string] 48 | $Name 49 | , 50 | [Parameter(ParameterSetName='Simple')] 51 | [string] 52 | $Value 53 | , 54 | [Parameter( 55 | ValueFromPipeline, 56 | ParameterSetName='Complex' 57 | )] 58 | [hashtable] 59 | $HashValues 60 | ) 61 | Begin 62 | { 63 | $f = $MyInvocation.InvocationName 64 | Write-Verbose -Message "$f - START" 65 | } 66 | 67 | Process 68 | { 69 | if ($PSCmdlet.ParameterSetName -eq "Simple") 70 | { 71 | $propHash = [PSCustomObject][ordered]@{ 72 | PSTypeName = "ARMvariable" 73 | $Name = $Value 74 | } 75 | } 76 | 77 | if ($PSCmdlet.ParameterSetName -eq "Complex") 78 | { 79 | $propHash = [PSCustomObject][ordered]@{ 80 | PSTypeName = "ARMvariable" 81 | $Name = [PSCustomObject]$HashValues 82 | } 83 | } 84 | $propHash 85 | } 86 | 87 | End 88 | { 89 | Write-Verbose -Message "$f - END" 90 | } 91 | 92 | } -------------------------------------------------------------------------------- /Tests/Get-ARMresourceScript.Tests.ps1: -------------------------------------------------------------------------------- 1 | #$here = Split-Path -Parent $MyInvocation.MyCommand.Path 2 | #$sut = (Split-Path -Leaf $MyInvocation.MyCommand.Path) -replace '\.Tests\.', '.' 3 | #. "$here\$sut" 4 | 5 | $modulePath = Split-Path $PSScriptRoot -Parent 6 | $modulepath = Join-Path -Path $modulePath -ChildPath posharm.psd1 7 | Import-Module $modulePath 8 | 9 | Describe "Get-ARMresourceScript" { 10 | 11 | Context "Single resource added to template" { 12 | 13 | New-ARMTemplate 14 | $expected = @{ 15 | APIversion = '2016-03-30' 16 | Name = 'MyVM' 17 | Type = 'Microsoft.Compute/virtualMachines' 18 | } 19 | 20 | New-ARMresource @expected | Add-ARMresource 21 | 22 | $resourceScript = Get-ARMtemplate | Select-Object -ExpandProperty resources | Get-ARMresourceScript 23 | $scriptBlock = [scriptblock]::Create($resourceScript) 24 | 25 | New-ARMTemplate 26 | 27 | It "Resources should be empty before script invoke" { 28 | (Get-ARMtemplate).resources.Count | Should be 0 29 | } 30 | 31 | It "Invoking script should not throw" { 32 | {$scriptBlock.Invoke()} | Should not throw 33 | } 34 | 35 | New-ARMTemplate 36 | $scriptBlock.Invoke() 37 | 38 | It "Invoking the script should create a new resource" { 39 | ((Get-ARMtemplate).resources | Measure-Object).Count | Should be 1 40 | } 41 | 42 | It "Getting the resource should not throw" { 43 | { Get-ARMtemplate | Select-Object -Property resources | Select-Object -First 1 } | Should not throw 44 | } 45 | 46 | $actualResource = Get-ARMtemplate | Select-Object -ExpandProperty resources | Select-Object -First 1 47 | 48 | It "Should create a resource with name [$($expected.Name)]" { 49 | $actualResource.Name | Should Be $expected.Name 50 | } 51 | 52 | It "Value of type should be [$($expected.Type)]" { 53 | $actualResource.Type | Should be $expected.Type 54 | } 55 | } 56 | } 57 | 58 | Remove-Module -name posharm -ErrorAction SilentlyContinue -------------------------------------------------------------------------------- /Tests/Get-ARMvariableScript.Tests.ps1: -------------------------------------------------------------------------------- 1 | #$here = Split-Path -Parent $MyInvocation.MyCommand.Path 2 | #$sut = (Split-Path -Leaf $MyInvocation.MyCommand.Path) -replace '\.Tests\.', '.' 3 | #. "$here\$sut" 4 | #Import-Module "C:\Users\tore\Dropbox\SourceTreeRepros\PoshARM\PoshARM.psd1" 5 | 6 | $modulePath = Split-Path $PSScriptRoot -Parent 7 | $modulepath = Join-Path -Path $modulePath -ChildPath posharm.psd1 8 | Import-Module $modulePath 9 | 10 | Describe "Get-ARMvariableScript" { 11 | 12 | Context "Single variable added to template" { 13 | 14 | New-ARMTemplate 15 | $expected = @{ 16 | Name = "test" 17 | Value = "foo-bar" 18 | } 19 | New-ARMvariable @expected | Add-ARMvariable 20 | 21 | $variableScript = Get-ARMtemplate | Select-Object -ExpandProperty variables | Get-ARMvariableScript 22 | $scriptBlock = [scriptblock]::Create($variableScript) 23 | 24 | New-ARMTemplate 25 | 26 | It "Variables should be empty before script invoke" { 27 | ((Get-ARMtemplate).variables.psobject.properties | Measure-Object).Count | Should be 0 28 | } 29 | 30 | It "Invoking script should not throw" { 31 | {$scriptBlock.Invoke()} | Should not throw 32 | } 33 | 34 | New-ARMTemplate 35 | $scriptBlock.Invoke() 36 | 37 | It "Invoking the script should create a new variable" { 38 | ((Get-ARMtemplate).variables.psobject.properties | Measure-Object).Count | Should be 1 39 | } 40 | 41 | It "Getting the variable should not throw" { 42 | { Get-ARMvariable -Name $expected.Name } | Should not throw 43 | } 44 | 45 | $actualVar = Get-ARMvariable -Name $expected.Name 46 | 47 | It "Should create a variable with name [$($expected.Name)]" { 48 | $actualVar.($expected.Name) | Should not be $null 49 | } 50 | 51 | It "Value of [$($expected.Name)] should be [$($expected.Value)]" { 52 | $actualVar.($expected.Name) | Should be $expected.Value 53 | } 54 | } 55 | } 56 | 57 | Remove-Module -name posharm -ErrorAction SilentlyContinue -------------------------------------------------------------------------------- /Tests/Get-ARMparameterScript.Tests.ps1: -------------------------------------------------------------------------------- 1 | #$here = Split-Path -Parent $MyInvocation.MyCommand.Path 2 | #$sut = (Split-Path -Leaf $MyInvocation.MyCommand.Path) -replace '\.Tests\.', '.' 3 | #. "$here\$sut" 4 | 5 | $modulePath = Split-Path $PSScriptRoot -Parent 6 | $modulepath = Join-Path -Path $modulePath -ChildPath posharm.psd1 7 | Import-Module $modulePath 8 | 9 | Describe "Get-ARMparameterScript" { 10 | 11 | Context "Single parameter added to template" { 12 | 13 | New-ARMTemplate 14 | $ExpectedParm = @{ 15 | Name = 'adminUserName' 16 | Type = 'string' 17 | } 18 | 19 | $actualParameter = New-ARMparameter @ExpectedParm | Add-ARMparameter 20 | 21 | $paramScript = Get-ARMtemplate | Select-Object -ExpandProperty parameters | Get-ARMparameterScript 22 | $scriptBlock = [scriptblock]::Create($paramScript) 23 | 24 | New-ARMTemplate 25 | 26 | It "Parameters should be empty before script invoke" { 27 | ((Get-ARMtemplate).parameters.psobject.properties | Measure-Object).Count | Should be 0 28 | } 29 | 30 | It "Invoking script should not throw" { 31 | {$scriptBlock.Invoke()} | Should not throw 32 | } 33 | 34 | New-ARMTemplate 35 | $scriptBlock.Invoke() 36 | 37 | It "Invoking the script should create a new variable" { 38 | ((Get-ARMtemplate).parameters.psobject.properties | Measure-Object).Count | Should be 1 39 | } 40 | 41 | It "Getting the parameter should not throw" { 42 | { Get-ARMparameter -Name $ExpectedParm.Name } | Should not throw 43 | } 44 | 45 | $actualParam = Get-ARMparameter -Name $ExpectedParm.Name 46 | 47 | It "Should create a parameter with name [$($ExpectedParm.Name)]" { 48 | $actualParam.($ExpectedParm.Name) | Should not be $null 49 | } 50 | 51 | It "Type of [$($ExpectedParm.Name)] should be [$($ExpectedParm.Type)]" { 52 | $actualParam.($ExpectedParm.Name).Type | Should be $ExpectedParm.Type 53 | } 54 | } 55 | } 56 | 57 | Remove-Module -name posharm -ErrorAction SilentlyContinue -------------------------------------------------------------------------------- /Functions/Set-ARMvariable.ps1: -------------------------------------------------------------------------------- 1 | #Requires -Version 5.0 2 | function Set-ARMvariable 3 | { 4 | <# 5 | .SYNOPSIS 6 | Update an existing variable in the ARM template 7 | 8 | .DESCRIPTION 9 | Update an existing variable in the ARM template 10 | 11 | .PARAMETER Name 12 | The variable Name. This is a Mandatory parameter and is an dynamic parameter 13 | 14 | .PARAMETER Value 15 | The new value for the variable. This is a Mandatory parameter 16 | 17 | .EXAMPLE 18 | Set-ARMvariable -Name nicName -Value "MyNewNic" 19 | 20 | This will set the variable nicName to value "MyNewNic" 21 | .INPUTS 22 | string 23 | 24 | .OUTPUTS 25 | 26 | .NOTES 27 | Author: Tore Groneng 28 | Website: www.firstpoint.no 29 | Twitter: @ToreGroneng 30 | #> 31 | 32 | [cmdletbinding()] 33 | Param( 34 | [Parameter(Mandatory)] 35 | $Value 36 | ) 37 | 38 | DynamicParam 39 | { 40 | $Dictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary 41 | 42 | $NewDynParam = @{ 43 | Name = "Name" 44 | Mandatory = $true 45 | ValueFromPipelineByPropertyName = $true 46 | ValueFromPipeline = $false 47 | DPDictionary = $Dictionary 48 | } 49 | 50 | $allVariables = $script:Template.variables.PSobject.Properties.Name 51 | 52 | if ($allVariables) 53 | { 54 | $null = $NewDynParam.Add("ValidateSet",$allVariables) 55 | } 56 | else 57 | { 58 | $null = $NewDynParam.Add("ValidateSet",@("-")) 59 | } 60 | 61 | New-DynamicParam @NewDynParam 62 | $Dictionary 63 | } 64 | 65 | Begin 66 | { 67 | $f = $MyInvocation.InvocationName 68 | Write-Verbose -Message "$f - START" 69 | } 70 | 71 | Process 72 | { 73 | $Name = $PSBoundParameters.Name 74 | 75 | $oldValue = Get-ARMVariable -Name $Name | Select-Object -ExpandProperty $Name 76 | Write-Verbose -Message "$f - Updating variable [$Name] from '$oldValue' to '$Value" 77 | 78 | if ($script:Template) 79 | { 80 | $script:Template.variables.$name = $Value 81 | } 82 | } 83 | 84 | End 85 | { 86 | Write-Verbose -Message "$f - END" 87 | 88 | } 89 | } -------------------------------------------------------------------------------- /Tests/Add-ARMresource.Tests.ps1: -------------------------------------------------------------------------------- 1 | #$here = Split-Path -Parent $MyInvocation.MyCommand.Path 2 | #$sut = (Split-Path -Leaf $MyInvocation.MyCommand.Path) -replace '\.Tests\.', '.' 3 | #. "$here\$sut" 4 | 5 | $modulePath = Split-Path $PSScriptRoot -Parent 6 | $modulepath = Join-Path -Path $modulePath -ChildPath posharm.psd1 7 | Import-Module $modulePath 8 | 9 | Describe "Add-ARMresource" { 10 | New-ArmTemplate 11 | 12 | $expected = @{ 13 | APIversion = '2016-03-30' 14 | Name = 'MyVM' 15 | Location = 'EAST-US' 16 | Tags = @{tag=1} 17 | Comments = 'hey' 18 | DependsOn = @("item1","item2") 19 | SKU = @{name="Standard_LRS"} 20 | Kind = 'storage' 21 | Properties = @{prop1=1} 22 | Type = 'Microsoft.Compute/virtualMachines' 23 | } 24 | 25 | $actual = New-ARMresource @expected 26 | 27 | Context "Without pipeline" { 28 | 29 | Add-ARMresource -InputObject $actual 30 | $template = Get-ARMTemplate 31 | 32 | It "Should add a resource to a template" { 33 | $template.resources.count | should be 1 34 | } 35 | 36 | $actualResource = $template.resources[0] 37 | 38 | It "Should have name property with value [$($expected.Name)]" { 39 | $actualResource.name | should be $expected.Name 40 | } 41 | 42 | It "Should be of type Array" { 43 | $template.resources -is [array] | Should be $true 44 | } 45 | } 46 | 47 | Context "With pipeline" { 48 | New-ArmTemplate 49 | 50 | $actual | Add-ARMresource 51 | $template = Get-ARMTemplate 52 | 53 | It "Should add a resource to a template" { 54 | $template.resources.count | should be 1 55 | } 56 | 57 | $actualResource = $null 58 | $actualResource = $template.resources[0] 59 | 60 | It "Should have name property with value [$($expected.Name)]" { 61 | $actualResource.name | should be $expected.Name 62 | } 63 | 64 | It "Should be of type Array" { 65 | $template.resources -is [array] | Should be $true 66 | } 67 | } 68 | } 69 | 70 | Remove-Module -name posharm -ErrorAction SilentlyContinue 71 | -------------------------------------------------------------------------------- /Functions/Get-ARMvariable.ps1: -------------------------------------------------------------------------------- 1 | #Requires -Version 5.0 2 | function Get-ARMvariable 3 | { 4 | <# 5 | .SYNOPSIS 6 | Get all variable or a specific one by name. 7 | 8 | .DESCRIPTION 9 | Get all variable or a specific one by name. 10 | 11 | .PARAMETER Name 12 | Name of the variable to get. This is a dynamic parameter. 13 | 14 | .EXAMPLE 15 | Get-ARMvariable 16 | 17 | This will return all variables in the ARM template 18 | .EXAMPLE 19 | Get-ARMvariable -Name nicName 20 | 21 | This will get the variable with name nicName 22 | 23 | .INPUTS 24 | String 25 | 26 | .OUTPUTS 27 | PSCustomObject 28 | 29 | .NOTES 30 | Author: Tore Groneng 31 | Website: www.firstpoint.no 32 | Twitter: @ToreGroneng 33 | #> 34 | [CmdletBinding()] 35 | [OutputType('PSCustomObject')] 36 | Param() 37 | 38 | DynamicParam 39 | { 40 | $Dictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary 41 | 42 | $NewDynParam = @{ 43 | Name = "Name" 44 | Mandatory = $false 45 | ValueFromPipelineByPropertyName = $true 46 | ValueFromPipeline = $false 47 | DPDictionary = $Dictionary 48 | } 49 | 50 | $allVariables = $script:Template.variables.PSobject.Properties.Name 51 | 52 | if ($allVariables) 53 | { 54 | $null = $NewDynParam.Add("ValidateSet",$allVariables) 55 | } 56 | else 57 | { 58 | $null = $NewDynParam.Add("ValidateSet",@("-")) 59 | } 60 | 61 | New-DynamicParam @NewDynParam 62 | $Dictionary 63 | } 64 | 65 | Begin 66 | { 67 | $f = $MyInvocation.InvocationName 68 | Write-Verbose -Message "$f - START" 69 | } 70 | 71 | Process 72 | { 73 | $Name = $PSBoundParameters.Name 74 | 75 | if (-not $PSBoundParameters.ContainsKey("Template")) 76 | { 77 | $Template = $script:Template 78 | } 79 | 80 | if ($Name) 81 | { 82 | Write-Verbose -Message "$f - Finding parameter with name [$Name]" 83 | $Template.variables | Select-Object -Property $Name 84 | } 85 | else 86 | { 87 | Write-Verbose -Message "$f - Returning all variables" 88 | $Template.variables 89 | } 90 | } 91 | 92 | End 93 | { 94 | Write-Verbose -Message "$f - START" 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /Functions/Add-ARMvariable.ps1: -------------------------------------------------------------------------------- 1 | #Requires -Version 5.0 2 | function Add-ARMvariable 3 | { 4 | <# 5 | .SYNOPSIS 6 | Add an ARM variable to an ARM template. 7 | 8 | .DESCRIPTION 9 | Add an ARM variable to an ARM template 10 | 11 | .PARAMETER InputObject 12 | The PSCustomObject of type 'ARMvariable' to be added to the ARM template. 13 | 14 | .PARAMETER Template 15 | The ARM template of type 'ARMtemplate' the variable should be added to. If not specified, it will add the variable to the module-level template 16 | 17 | .EXAMPLE 18 | $nicName = New-ARMvariable -Name nicName -Value "myNIC" 19 | Add-ARMvariable -InputObject $nicName 20 | 21 | This will add the variable nicName to the module level template 22 | 23 | .EXAMPLE 24 | New-ARMvariable -Name nicName -Value "myNIC" | Add-ARMvariable -Template $myTemplate 25 | 26 | This will add the variable nicName to the userdefined template $myTemplate 27 | 28 | .INPUTS 29 | PSCustomObject 30 | 31 | .OUTPUTS 32 | PSCustomObject 33 | 34 | .NOTES 35 | Author: Tore Groneng 36 | Website: www.firstpoint.no 37 | Twitter: @ToreGroneng 38 | #> 39 | [cmdletbinding()] 40 | Param( 41 | [Parameter(Mandatory, ValueFromPipeline)] 42 | [PSTypeName('ARMvariable')] 43 | $InputObject 44 | , 45 | [PSTypeName('ARMtemplate')] 46 | $Template 47 | , 48 | [switch] 49 | $PassThru 50 | ) 51 | Begin 52 | { 53 | $f = $MyInvocation.InvocationName 54 | Write-Verbose -Message "$f - START" 55 | } 56 | 57 | Process 58 | { 59 | Write-Verbose -Message "$f - Processing" 60 | 61 | if (-not $Template) 62 | { 63 | Write-Verbose -Message "$f - Using module level template" 64 | $Template = $script:Template 65 | } 66 | 67 | if ($Template) 68 | { 69 | Write-Verbose -Message "$f - Have a template" 70 | foreach ($prop in $InputObject.PSobject.Properties) 71 | { 72 | $value = $prop.Value 73 | Write-Verbose -Message "$f - Processing property $($prop.Name)" 74 | $Template.variables | Add-Member -MemberType NoteProperty -Name $prop.Name -Value $value 75 | } 76 | } 77 | 78 | if ($PassThru.IsPresent) 79 | { 80 | $InputObject 81 | } 82 | } 83 | 84 | End 85 | { 86 | Write-Verbose -Message "$f - End" 87 | } 88 | 89 | } -------------------------------------------------------------------------------- /Functions/Get-ARMparameter.ps1: -------------------------------------------------------------------------------- 1 | #Requires -Version 5.0 2 | function Get-ARMparameter 3 | { 4 | <# 5 | .SYNOPSIS 6 | Get all parameters or a specific one by name. 7 | 8 | .DESCRIPTION 9 | Get all parameters or a specific one by name. 10 | 11 | .PARAMETER Name 12 | Name of the parameter to get. This is a dynamic parameter. 13 | 14 | .EXAMPLE 15 | Get-ARMparameter 16 | 17 | This will return all parameters in the ARM template 18 | .EXAMPLE 19 | Get-ARMparameter -Name adminUsername 20 | 21 | This will get the parameter with name adminUsername 22 | 23 | .INPUTS 24 | String 25 | 26 | .OUTPUTS 27 | PSCustomObject 28 | 29 | .NOTES 30 | Author: Tore Groneng 31 | Website: www.firstpoint.no 32 | Twitter: @ToreGroneng 33 | #> 34 | [CmdletBinding()] 35 | [OutputType('PSCustomObject')] 36 | Param () 37 | 38 | DynamicParam 39 | { 40 | $Dictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary 41 | 42 | $NewDynParam = @{ 43 | Name = "Name" 44 | Mandatory = $false 45 | ValueFromPipelineByPropertyName = $true 46 | ValueFromPipeline = $false 47 | DPDictionary = $Dictionary 48 | } 49 | 50 | $allParameters = $script:Template.parameters.PSobject.Properties.Name 51 | 52 | if ($allParameters) 53 | { 54 | $null = $NewDynParam.Add("ValidateSet",$allParameters) 55 | } 56 | else 57 | { 58 | $null = $NewDynParam.Add("ValidateSet",@("-")) 59 | } 60 | 61 | New-DynamicParam @NewDynParam 62 | $Dictionary 63 | } 64 | 65 | BEGIN 66 | { 67 | $f = $MyInvocation.InvocationName 68 | Write-Verbose -Message "$f - START" 69 | } 70 | 71 | PROCESS 72 | { 73 | $Name = $PSBoundParameters.Name 74 | 75 | if (-not $PSBoundParameters.ContainsKey("Template")) 76 | { 77 | $Template = $script:Template 78 | } 79 | 80 | if ($Name) 81 | { 82 | Write-Verbose -Message "$f - Finding parameter with name [$Name]" 83 | 84 | $Template.parameters | Select-Object -Property $Name 85 | } 86 | else 87 | { 88 | Write-Verbose -Message "$f - Returning all parameters" 89 | $Template.parameters 90 | } 91 | } 92 | 93 | END 94 | { 95 | Write-Verbose -Message "$f - START" 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /Functions/Add-ARMparameter.ps1: -------------------------------------------------------------------------------- 1 | #Requires -Version 5.0 2 | function Add-ARMparameter 3 | { 4 | <# 5 | .SYNOPSIS 6 | Add an ARM parameter to an ARM template. 7 | 8 | .DESCRIPTION 9 | Add an ARM parameter to an ARM template 10 | 11 | .PARAMETER InputObject 12 | The PSCustomObject of type 'ARMparameter' to be added to the ARM template. 13 | 14 | .PARAMETER Template 15 | The ARM template of type 'ARMtemplate' the parameter should be added to. If not specified, it will add the parameter to the module-level template 16 | 17 | .EXAMPLE 18 | $adminUser = New-ARMparameter -Name adminUsername -Type String 19 | Add-ARMparameter -InputObject $adminUser 20 | 21 | This will add the parameter adminUsername to the module level template 22 | 23 | .EXAMPLE 24 | New-ARMparameter -Name adminUsername -Type String | Add-ARMparameter -Template $myTemplate 25 | 26 | This will add the parameter adminUsername to the userdefined template $myTemplate 27 | 28 | .INPUTS 29 | PSCustomObject 30 | 31 | .OUTPUTS 32 | PSCustomObject 33 | 34 | .NOTES 35 | Author: Tore Groneng 36 | Website: www.firstpoint.no 37 | Twitter: @ToreGroneng 38 | #> 39 | 40 | [cmdletbinding()] 41 | Param( 42 | [Parameter(Mandatory, ValueFromPipeline)] 43 | [PSTypeName('ARMparameter')] 44 | $InputObject 45 | , 46 | [PSTypeName('ARMtemplate')] 47 | $Template 48 | , 49 | [switch] 50 | $PassThru 51 | ) 52 | 53 | Begin 54 | { 55 | $f = $MyInvocation.InvocationName 56 | Write-Verbose -Message "$f - START" 57 | } 58 | 59 | Process 60 | { 61 | Write-Verbose -Message "$f - Processing" 62 | 63 | if (-not $Template) 64 | { 65 | Write-Verbose -Message "$f - Using module level template" 66 | $Template = $script:Template 67 | } 68 | 69 | if ($Template) 70 | { 71 | Write-Verbose -Message "$f - Have a template" 72 | foreach ($prop in $InputObject.PSobject.Properties) 73 | { 74 | $value = $prop.Value 75 | Write-Verbose -Message "$f - Processing property $($prop.Name)" 76 | $Template.parameters | Add-Member -MemberType NoteProperty -Name $prop.Name -Value $value 77 | } 78 | } 79 | else 80 | { 81 | Write-Verbose -Message "$f - Template not found" 82 | } 83 | 84 | if ($PassThru.IsPresent) 85 | { 86 | $InputObject 87 | } 88 | } 89 | 90 | End 91 | { 92 | Write-Verbose -Message "$f - End" 93 | } 94 | } -------------------------------------------------------------------------------- /Functions/Import-ARMtemplate.ps1: -------------------------------------------------------------------------------- 1 | #Requires -Version 5.0 2 | function Import-ARMtemplate 3 | { 4 | <# 5 | .SYNOPSIS 6 | Import an ARM template. 7 | 8 | .DESCRIPTION 9 | This Cmdlet imports an ARM template from a file or from a JSON-string. 10 | 11 | .PARAMETER JsonString 12 | A JSON string that is an ARM template 13 | 14 | .PARAMETER FileName 15 | A file that contains a JSON ARM template 16 | 17 | .EXAMPLE 18 | Get-ChildItem -Path .\TestFiles\SimpleVM.json | Import-ARMtemplate 19 | Get-ARMtemplate 20 | 21 | .EXAMPLE 22 | # Copy an ARM template to the clipboard 23 | 24 | Get-ClipBoard | Import-ARMtemplate 25 | Get-ARMtemplate 26 | 27 | .EXAMPLE 28 | Import-ARMtemplate -FileName .\TestFiles\SimpleVM.json 29 | Get-ARMtemplate 30 | 31 | .INPUTS 32 | string 33 | 34 | .OUTPUTS 35 | 36 | 37 | .NOTES 38 | Author: Tore Groneng 39 | Website: www.firstpoint.no 40 | Twitter: @ToreGroneng 41 | #> 42 | [cmdletbinding()] 43 | Param( 44 | [Parameter(ValueFromPipeline, ParameterSetName="AsString")] 45 | [string] 46 | $JsonString 47 | , 48 | [Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName, ParameterSetName="FromFile")] 49 | [System.IO.FileInfo] 50 | [Alias("FullName")] 51 | $FileName 52 | , 53 | [switch]$PassThru 54 | ) 55 | 56 | Begin 57 | { 58 | $f = $MyInvocation.InvocationName 59 | Write-Verbose -Message "$f - START" 60 | $sb = New-Object System.Text.StringBuilder 61 | } 62 | 63 | Process 64 | { 65 | if ($PSBoundParameters.ContainsKey("FileName")) 66 | { 67 | $fileNameType = $FileName.GetType().Name 68 | if ($fileNameType -eq 'FileInfo') 69 | { 70 | $FileName = $FileName.FullName 71 | } 72 | $JsonString = Get-Content -Path $FileName -Encoding UTF8 73 | } 74 | 75 | if ($JsonString) 76 | { 77 | $null = $sb.Append($JsonString + [environment]::NewLine) 78 | } 79 | } 80 | 81 | End 82 | { 83 | if ($sb.Length -gt 0) 84 | { 85 | $jsonString = $sb.ToString() 86 | Write-Verbose -Message "$f - $jsonString" 87 | 88 | $templateObject = $jsonString | ConvertFrom-Json 89 | 90 | if ($PassThru.IsPresent) 91 | { 92 | $templateObject 93 | } 94 | else 95 | { 96 | $script:Template = $templateObject 97 | } 98 | } 99 | } 100 | 101 | } -------------------------------------------------------------------------------- /Tests/Out-HashString.Tests.ps1: -------------------------------------------------------------------------------- 1 | #$here = Split-Path -Parent $MyInvocation.MyCommand.Path 2 | #$sut = (Split-Path -Leaf $MyInvocation.MyCommand.Path) -replace '\.Tests\.', '.' 3 | #. "$here\$sut" 4 | 5 | $modulePath = Split-Path $PSScriptRoot -Parent 6 | $modulepath = Join-Path -Path $modulePath -ChildPath poshARM.psd1 7 | Import-Module $modulePath 8 | 9 | Describe "Out-HashString" { 10 | 11 | Context "Without Pipeline" { 12 | It "Should return an empty hashtable if input is not a hashtable" { 13 | $expected = "@{}" 14 | $actual = Out-HashString -InputObject "foo" 15 | $actual | Should Be $Expected 16 | } 17 | 18 | It "Should convert a hashtabel to a string representation" { 19 | $var = @{Test="keyValue"} 20 | $Expected = '@{\r\n Test = "keyValue"\r\n}' -replace ([regex]::Escape("\r\n")), [environment]::NewLine 21 | $actual = Out-HashString -InputObject $var 22 | $actual | Should Be $Expected 23 | } 24 | 25 | It "Should handle nested hashtables" { 26 | $var = @{Test="keyValue";Nested = @{Key1="Value1";Key2="Value2"}} 27 | $Expected = '@{\r\n Test = "keyValue"\r\n Nested = @{\r\n Key1 = "Value1"\r\n Key2 = "Value2"\r\n }\r\n}' -replace ([regex]::Escape("\r\n")), [environment]::NewLine 28 | $actual = Out-HashString -InputObject $var 29 | $actual | Should Be $Expected 30 | } 31 | } 32 | 33 | Context "With Pipeline" { 34 | It "Should return an empty hashtable if input is not a hashtable" { 35 | $expected = "@{}" 36 | $actual = "foo" | Out-HashString 37 | $actual | Should Be $Expected 38 | } 39 | 40 | It "Should convert a hashtabel to a string representation" { 41 | $var = @{Test="keyValue"} 42 | $Expected = '@{\r\n Test = "keyValue"\r\n}' -replace ([regex]::Escape("\r\n")), [environment]::NewLine 43 | $actual = $var | Out-HashString 44 | $actual | Should Be $Expected 45 | } 46 | 47 | It "Should handle nested hashtables" { 48 | $var = @{Test="keyValue";Nested = @{Key1="Value1";Key2="Value2"}} 49 | $Expected = '@{\r\n Test = "keyValue"\r\n Nested = @{\r\n Key1 = "Value1"\r\n Key2 = "Value2"\r\n }\r\n}' -replace ([regex]::Escape("\r\n")), [environment]::NewLine 50 | $actual = $var | Out-HashString 51 | $actual | Should Be $Expected 52 | } 53 | } 54 | } 55 | 56 | Remove-Module -name posharm -ErrorAction SilentlyContinue -------------------------------------------------------------------------------- /Tests/New-ARMvariable.Tests.ps1: -------------------------------------------------------------------------------- 1 | #$here = Split-Path -Parent $MyInvocation.MyCommand.Path 2 | #$sut = (Split-Path -Leaf $MyInvocation.MyCommand.Path) -replace '\.Tests\.', '.' 3 | #. "$here\$sut" 4 | #$path = "C:\Users\tore\Dropbox\SourceTreeRepros\PoshARM\PoshARM.psd1" 5 | #Import-Module $path 6 | 7 | $modulePath = Split-Path $PSScriptRoot -Parent 8 | $modulepath = Join-Path -Path $modulePath -ChildPath poshARM.psd1 9 | Import-Module $modulePath 10 | 11 | Describe "New-ARMvariable" { 12 | 13 | Context "Simple name value tests" { 14 | $Expected = @{ 15 | Name = "test" 16 | Value = "foo-bar" 17 | } 18 | $ActualVar = New-ARMvariable @Expected 19 | 20 | It "Should create a PSCustomObject" { 21 | $ActualVar.GetType().Name | Should Be "PSCustomObject" 22 | } 23 | 24 | It "Should create a PSCustomObject with PStypeName 'ARMvariable'" { 25 | $ActualVar.pstypenames[0] | should be "ARMvariable" 26 | } 27 | 28 | It "Should create a property [$($Expected.Name)]" { 29 | $ActualVar.($Expected.Name) | should not be $null 30 | } 31 | 32 | It "Property [$($Expected.Name)] should have value [$($Expected.Value)]" { 33 | $ActualVar.($Expected.Name) | should be $Expected.Value 34 | } 35 | } 36 | 37 | Context "Value from a hashtable" { 38 | $name = "subnet" 39 | $expected = @{ 40 | Name = "NameKey" 41 | Ipaddress = "10.0.0.2" 42 | SubNet = "255.255.255.0" 43 | } 44 | 45 | $ActualVar = New-ARMvariable -Name $name -HashValues $expected 46 | 47 | It "Should create a PSCustomObject" { 48 | $ActualVar.GetType().Name | Should Be "PSCustomObject" 49 | } 50 | 51 | It "Should create a PSCustomObject with PStypeName 'ARMvariable'" { 52 | $ActualVar.pstypenames[0] | should be "ARMvariable" 53 | } 54 | 55 | It "Should have a Property [$name]" { 56 | $ActualVar | Select-Object -ExpandProperty $name | Should not be $null 57 | } 58 | 59 | $actual = $ActualVar.$name 60 | 61 | It "Should have 3 properties" { 62 | $ActualVar.$name.psobject.Properties.Name.Count | should be 3 63 | } 64 | 65 | It "Value of [Name] should be [$($expected.Name)]" { 66 | $actual.Name | Should be $expected.Name 67 | } 68 | 69 | It "Value of [Ipaddress] should be [$($expected.Ipaddress)]" { 70 | $actual.Ipaddress | Should be $expected.Ipaddress 71 | } 72 | 73 | It "Value of [SubNet] should be ]$($expected.SubNet)]" { 74 | $actual.SubNet | Should be $expected.SubNet 75 | } 76 | } 77 | } 78 | 79 | Remove-Module -Name PoshARM -ErrorAction SilentlyContinue -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PoshARM 2 | 3 | ## What is this? 4 | A Powershell module for Azure ARM templates. You can parse and create Azure ARM templates with this module. 5 | 6 | ## Cmdlets implemented 7 | 8 | #### Add-ARMparameter** 9 | 10 | > Add an ARM parameter to an ARM template. 11 | 12 | #### Add-ARMresource** 13 | 14 | > Add an ARM resource to an ARM template. 15 | 16 | #### Add-ARMvariable** 17 | 18 | > Add an ARM variable to an ARM template. 19 | 20 | #### ConvertTo-Hash** 21 | 22 | > Converts a PScustomobject to a hashtable. 23 | 24 | #### Get-ARMparameter** 25 | 26 | > Get all parameters or a specific one by name. 27 | 28 | #### Get-ARMparameterScript** 29 | 30 | > Get the Powershell script that will recreate the parameteres in the ARM template. 31 | 32 | #### Get-ARMresourceList** 33 | 34 | > Get a list of resource providers in Azure. 35 | 36 | #### Get-ARMresourceScript** 37 | 38 | > Get the Powershell script that will recreate the resources in the ARM template. 39 | 40 | #### Get-ARMtemplate** 41 | 42 | > Get the ARM template defined at the module level. 43 | 44 | #### Get-ARMtemplateScript** 45 | 46 | > Get the Powershell script that will recreate the ARM template. 47 | 48 | #### Get-ARMvariable** 49 | 50 | > Get all variable or a specific one by name. 51 | 52 | #### Get-ARMvariableScript** 53 | 54 | > Get the Powershell script that will recreate the variables in the ARM template. 55 | 56 | #### Import-ARMtemplate** 57 | 58 | > Import an ARM template. 59 | 60 | #### New-ARMparameter** 61 | 62 | > Create a new ARM template parameter. 63 | 64 | #### New-ARMresource** 65 | 66 | > Create a new ARM template resource. 67 | 68 | #### New-ARMTemplate** 69 | 70 | > Create a new blank ARM template. 71 | 72 | #### New-ARMvariable** 73 | 74 | > Create a new ARM template variable. 75 | 76 | #### Out-HashString** 77 | 78 | > Convert an hashtable or and OrderedDictionary to a string. 79 | 80 | #### Set-ARMmetadata** 81 | 82 | > Creates a metadata.json file for your ARM template. 83 | 84 | #### Set-ARMparameter** 85 | 86 | > Update an existing parameter in the ARM template. 87 | 88 | #### Set-ARMvariable** 89 | 90 | > Update an existing variable in the ARM template. 91 | 92 | #### Update-ARMresourceList** 93 | 94 | > This will update the allResources.json file that is used as input when creating a New-ARMresource 95 | 96 | 97 | 98 | ## Guide 99 | 100 | You can find a guide on my blog at [PoshARM blog guide!](http://asaconsultant.blogspot.no/2017/01/something-completely-different-posharm.html). 101 | 102 | 103 | ## Status 104 | Current status: Under development 105 | 106 | 107 | ## PowershellGallery 108 | Published to PowershellGallery: [https://www.powershellgallery.com/packages/posharm](https://www.powershellgallery.com/packages/posharm) 109 | -------------------------------------------------------------------------------- /Functions/Set-ARMparameter.ps1: -------------------------------------------------------------------------------- 1 | #Requires -Version 5.0 2 | function Set-ARMparameter 3 | { 4 | <# 5 | .SYNOPSIS 6 | Update an existing parameter in the ARM template 7 | 8 | .DESCRIPTION 9 | Update an existing parameter in the ARM template 10 | 11 | .PARAMETER Name 12 | The variable Name. This is a Mandatory parameter and is an dynamic parameter 13 | 14 | .PARAMETER Value 15 | The new value for the variable. This is a Mandatory parameter 16 | 17 | .EXAMPLE 18 | Set-ARMparameter -Name adminUsername -Value [PSCustomObject]@{type="string"} 19 | 20 | This will update the parameter adminUsername with a type of string 21 | .EXAMPLE 22 | $newParameter = New-ARMparameter -Name adminUsername -Type string -DefaultValue "foo" 23 | $newParameter | Add-ARMparameter 24 | $newParameter.adminUsername.defaultValue = "new foo" 25 | 26 | Set-ARMparameter -Name adminUsername -Value $newParameter 27 | 28 | This will create a new parameter adminUsername and add it to the ARM template. The defaultValue 29 | is updated to "new foo" and the value of the adminUsername parameter is updated. 30 | 31 | .INPUTS 32 | PSCustomObject 33 | 34 | .OUTPUTS 35 | 36 | .NOTES 37 | Author: Tore Groneng 38 | Website: www.firstpoint.no 39 | Twitter: @ToreGroneng 40 | #> 41 | [cmdletbinding()] 42 | Param( 43 | [Parameter(Mandatory)] 44 | [PSCustomObject] 45 | $Value 46 | ) 47 | DynamicParam 48 | { 49 | $Dictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary 50 | 51 | $NewDynParam = @{ 52 | Name = "Name" 53 | Mandatory = $true 54 | ValueFromPipelineByPropertyName = $true 55 | ValueFromPipeline = $false 56 | DPDictionary = $Dictionary 57 | } 58 | 59 | $allParameters = $script:Template.parameters.PSobject.Properties.Name 60 | 61 | if ($allParameters) 62 | { 63 | $null = $NewDynParam.Add("ValidateSet",$allParameters) 64 | } 65 | else 66 | { 67 | $null = $NewDynParam.Add("ValidateSet",@("-")) 68 | } 69 | 70 | New-DynamicParam @NewDynParam 71 | $Dictionary 72 | } 73 | # FIXME this cmdlet should reflect the parameters of New-ARMparameter 74 | Begin 75 | { 76 | $f = $MyInvocation.InvocationName 77 | Write-Verbose -Message "$f - START" 78 | } 79 | 80 | Process 81 | { 82 | $Name = $PSBoundParameters.Name 83 | 84 | $oldvalue = ((Get-ARMparameter -Name $Name | Select-Object -ExpandProperty $name | ConvertTo-Hash | Out-HashString) -replace [environment]::NewLine,"") -replace " ","" 85 | $newValue = (($value | ConvertTo-Hash | Out-HashString) -replace [environment]::NewLine,"") -replace " ","" 86 | Write-Verbose -Message "$f - Updating variable [$Name] from '$oldValue' to '$newValue" 87 | 88 | if ($script:Template) 89 | { 90 | $script:Template.parameters.$name = $Value 91 | } 92 | } 93 | 94 | } -------------------------------------------------------------------------------- /Tests/New-ARMresource.Tests.ps1: -------------------------------------------------------------------------------- 1 | #$here = Split-Path -Parent $MyInvocation.MyCommand.Path 2 | #$sut = (Split-Path -Leaf $MyInvocation.MyCommand.Path) -replace '\.Tests\.', '.' 3 | #. "$here\$sut" 4 | #Import-Module C:\Users\tore\Dropbox\SourceTreeRepros\PoshARM 5 | $modulePath = Split-Path $PSScriptRoot -Parent 6 | $modulepath = Join-Path -Path $modulePath -ChildPath poshARM.psd1 7 | Import-Module $modulePath 8 | 9 | Describe "New-ARMresource" { 10 | 11 | $expected = @{ 12 | APIversion = '2016-03-30' 13 | Name = 'MyVM' 14 | Location = 'EAST-US' 15 | Tags = @{tag=1} 16 | Comments = 'hey' 17 | DependsOn = @("item1","item2") 18 | SKU = @{value = "skuvalue"} 19 | Kind = 'storage' 20 | Properties = @{prop1=1} 21 | Type = 'Microsoft.Compute/virtualMachines' 22 | } 23 | 24 | $Actual = New-ARMresource @expected 25 | 26 | Context "Create object" { 27 | 28 | It "Should create a new resource object" { 29 | $Actual | Should not Be $null 30 | } 31 | 32 | It "Should be of type [PSCustomObject]" { 33 | $Actual.GetType().Name | Should be "PSCustomObject" 34 | } 35 | 36 | It "Should create a PSCustomObject with PStypeName 'ARMresource'" { 37 | $Actual.pstypenames[0] | should be "ARMresource" 38 | } 39 | 40 | It "Should have an APIversion [$($expected.APIversion)]" { 41 | $Actual.APIversion | Should be $expected.APIversion 42 | } 43 | 44 | It "Should have an Name [$($expected.Name)]" { 45 | $Actual.Name | Should be $expected.Name 46 | } 47 | 48 | It "Should have an Location [$($expected.Location)]" { 49 | $Actual.Location | Should be $expected.Location 50 | } 51 | 52 | It "Should have a Tag [$($expected.Tags.tag)]" { 53 | $Actual.tags.tag | Should be $expected.Tags.tag 54 | } 55 | 56 | It "Should have a Comment [$($expected.Comments)]" { 57 | $Actual.Comments | Should be $expected.Comments 58 | } 59 | 60 | It "Should have a DependsOn [$($expected.DependsOn)]" { 61 | $Actual.DependsOn | Should be $expected.DependsOn 62 | } 63 | 64 | It "Should have a SKU [$($expected.SKU)]" { 65 | $Actual.SKU | Should be $expected.SKU 66 | } 67 | 68 | It "Should have a Kind [$($expected.Kind)]" { 69 | $Actual.Kind | Should be $expected.Kind 70 | } 71 | 72 | It "Should have a Properties [$($expected.Properties.Prop)]" { 73 | $Actual.Properties.Prop | Should be $expected.Properties.Prop 74 | } 75 | 76 | It "Should have a Type [$($expected.Type)]" { 77 | $Actual.Type | Should be $expected.Type 78 | } 79 | } 80 | } 81 | 82 | Remove-Module -name posharm -ErrorAction SilentlyContinue -------------------------------------------------------------------------------- /Tests/Import-ARMtemplate.Tests.ps1: -------------------------------------------------------------------------------- 1 | #$here = Split-Path -Parent $MyInvocation.MyCommand.Path 2 | #$sut = (Split-Path -Leaf $MyInvocation.MyCommand.Path) -replace '\.Tests\.', '.' 3 | #. "$here\$sut" 4 | 5 | $modulePath = Split-Path $PSScriptRoot -Parent 6 | $modulepath = Join-Path -Path $modulePath -ChildPath posharm.psd1 7 | Import-Module $modulePath 8 | 9 | Describe "Import-ARMtemplate" { 10 | $modulepath = Split-Path $PSScriptRoot -Parent 11 | $testfilePath = Join-Path -Path $modulepath -ChildPath "TestFiles\simplevm.json" 12 | 13 | $jsonString = Get-Content -Path $testfilePath -Encoding UTF8 -Raw 14 | 15 | It "JSON string should not be null" { 16 | $jsonString | Should not be $null 17 | } 18 | 19 | It "TestFilePath should exists" { 20 | Test-Path -Path $testfilePath | Should be $true 21 | } 22 | 23 | Context "After module loaded" { 24 | It "Should not have an ARM template after module Import" { 25 | ((Get-ARMtemplate).PSobject.Properties | Measure-Object).Count | should be 0 26 | } 27 | } 28 | 29 | Context "Importing template from file" { 30 | 31 | Import-ARMtemplate -FileName $testfilePath 32 | 33 | It "Should import a template" { 34 | ((Get-ARMtemplate).PSobject.Properties | Measure-Object).Count | Should BeGreaterThan 0 35 | } 36 | } 37 | 38 | Remove-Module PoshARM 39 | Import-Module $modulePath 40 | 41 | Context "Importing template from file with pipeline" { 42 | 43 | Get-ChildItem -path $testfilePath | Import-ARMtemplate 44 | 45 | It "Should import a template" { 46 | ((Get-ARMtemplate).PSobject.Properties | Measure-Object).Count | Should BeGreaterThan 0 47 | } 48 | } 49 | 50 | Remove-Module PoshARM 51 | Import-Module $modulePath 52 | 53 | Context "Importing template from a string variable" { 54 | 55 | Import-ARMtemplate -JsonString $jsonString 56 | 57 | It "Should import a template" { 58 | ((Get-ARMtemplate).PSobject.Properties | Measure-Object).Count | Should BeGreaterThan 0 59 | } 60 | } 61 | 62 | Remove-Module PoshARM 63 | Import-Module $modulePath 64 | 65 | Context "Importing template from a string variable with pipeline" { 66 | 67 | $jsonString | Import-ARMtemplate 68 | 69 | It "Should import a template" { 70 | ((Get-ARMtemplate).PSobject.Properties | Measure-Object).Count | Should BeGreaterThan 0 71 | } 72 | } 73 | 74 | Remove-Module PoshARM 75 | Import-Module $modulePath 76 | 77 | Context "Importing template from clipboard" { 78 | $jsonString | Set-Clipboard 79 | Get-Clipboard | Import-ARMtemplate 80 | 81 | It "Should import a template" { 82 | ((Get-ARMtemplate).PSobject.Properties | Measure-Object).Count | Should BeGreaterThan 0 83 | } 84 | } 85 | 86 | } 87 | 88 | Remove-Module -name posharm -ErrorAction SilentlyContinue -------------------------------------------------------------------------------- /Functions/New-ARMparameter.ps1: -------------------------------------------------------------------------------- 1 | #Requires -Version 5.0 2 | function New-ARMparameter 3 | { 4 | <# 5 | .SYNOPSIS 6 | Create a new ARM template parameter 7 | 8 | .DESCRIPTION 9 | Create a new ARM template parameter 10 | 11 | .PARAMETER Name 12 | The name of the parameter. This is Mandatory 13 | 14 | .PARAMETER Type 15 | The parameter type. Allowed values are "string","secureString","int","bool","object","secureObject","array" 16 | 17 | .EXAMPLE 18 | New-ARMparameter -Name adminUsername -Type String 19 | 20 | This will create a new parameter named adminUsername of type String 21 | 22 | .EXAMPLE 23 | $AllowedValues = @{ 24 | AllowedValues = "2008-R2-SP1","2012-Datacenter","2012-R2-Datacenter","2016-Nano-Server","2016-Datacenter-with-Containers","2016-Datacenter" 25 | } 26 | New-ARMparameter -Name windowsOSVersion -Type String -DefaultValue "2016-Datacenter" @allowedValues 27 | 28 | This will create a new parameter named windowsOSVersion of type String, with a default value of "2016-Datacenter" which 29 | is in the allowedValues list/array 30 | 31 | .INPUTS 32 | String 33 | 34 | .OUTPUTS 35 | PSCustomObject 36 | 37 | .NOTES 38 | Author: Tore Groneng 39 | Website: www.firstpoint.no 40 | Twitter: @ToreGroneng 41 | #> 42 | 43 | [cmdletbinding()] 44 | Param( 45 | [Parameter(Mandatory)] 46 | [string] 47 | $Name 48 | , 49 | [Parameter(Mandatory)] 50 | [ValidateSet("string","secureString","int","bool","object","secureObject","array")] 51 | [string] 52 | $Type 53 | , 54 | $DefaultValue 55 | , 56 | $AllowedValues 57 | , 58 | $MinValue 59 | , 60 | $MaxValue 61 | , 62 | $MinLength 63 | , 64 | $MaxLength 65 | , 66 | $Description 67 | , 68 | $Metadata 69 | ) 70 | 71 | $propHash = [ordered]@{ 72 | type = $Type 73 | } 74 | 75 | if ($DefaultValue) 76 | { 77 | $null = $propHash.defaultValue = $DefaultValue 78 | } 79 | 80 | if ($AllowedValues) 81 | { 82 | $null = $propHash.allowedValues = $AllowedValues 83 | } 84 | 85 | if ($MinValue) 86 | { 87 | $null = $propHash.minValue = $MinValue 88 | } 89 | 90 | if ($MaxValue) 91 | { 92 | $null = $propHash.maxValue = $MaxValue 93 | } 94 | 95 | if ($MinLength) 96 | { 97 | $null = $propHash.minLength = $MinLength 98 | } 99 | 100 | if ($MaxLength) 101 | { 102 | $null = $propHash.maxLength = $MaxLength 103 | } 104 | 105 | if ($Description) 106 | { 107 | Write-Verbose -Message "Adding description" 108 | $null = $propHash.metadata = @{description = $Description} 109 | } 110 | 111 | if ($Metadata) 112 | { 113 | $null = $propHash.metadata += $Metadata 114 | } 115 | 116 | $out = [PSCustomobject]@{ 117 | PSTypeName = "ARMparameter" 118 | $Name = [PSCustomobject]$propHash 119 | } 120 | 121 | $out 122 | 123 | } -------------------------------------------------------------------------------- /Functions/ConvertTo-Hash.ps1: -------------------------------------------------------------------------------- 1 | #Requires -Version 5.0 2 | function ConvertTo-Hash 3 | { 4 | <# 5 | .SYNOPSIS 6 | Converts a PScustomobject to a hashtable 7 | 8 | .DESCRIPTION 9 | Converts a PScustomobject to a hashtable 10 | 11 | .PARAMETER InputObject 12 | The PSCustomObject you want to convert to a hashtable 13 | 14 | .EXAMPLE 15 | $obj = [PSCustomobject]@{ 16 | Name = "Tore" 17 | Value = "Test" 18 | } 19 | 20 | $obj | ConvertTo-Hash 21 | 22 | This will create a hashtable with keys matching the properties of the object. 23 | 24 | .INPUTS 25 | PSCustomObject 26 | 27 | .OUTPUTS 28 | hashtable 29 | 30 | .NOTES 31 | Author: Tore Groneng 32 | Website: www.firstpoint.no 33 | Twitter: @ToreGroneng 34 | #> 35 | [cmdletbinding()] 36 | Param( 37 | [Parameter(ValueFromPipeline)] 38 | [PSCustomObject]$InputObject 39 | ) 40 | Begin{ 41 | $f = $MyInvocation.InvocationName 42 | Write-Verbose -Message "$f - START" 43 | } 44 | Process 45 | { 46 | Write-Verbose -Message "$F - processing $($inputobject.GetType().Name)" 47 | if ($InputObject -is [array]) 48 | { 49 | Write-Verbose -Message "Is array object" 50 | foreach ($item in $value) 51 | { 52 | $item | ConvertTo-Hash 53 | } 54 | } 55 | 56 | if ($InputObject -is [hashtable] -or $InputObject -is [System.Collections.Specialized.OrderedDictionary]) 57 | { 58 | return $InputObject 59 | } 60 | 61 | $hash = [ordered]@{} 62 | 63 | if ($InputObject -is [System.Management.Automation.PSCustomObject]) 64 | { 65 | Write-Verbose -Message "$f - Processing [pscustomobject]" 66 | 67 | foreach ($prop in $InputObject.psobject.Properties) 68 | { 69 | $name = $prop.Name 70 | $value = $prop.Value 71 | Write-Verbose -Message "$f - Property [$name]" 72 | 73 | 74 | if ($value -is [System.Management.Automation.PSCustomObject]) 75 | { 76 | Write-Verbose -Message "$f - Value is PScustomobject" 77 | $value = $value | ConvertTo-Hash 78 | } 79 | 80 | if ($value -is [array]) 81 | { 82 | Write-Verbose -Message "Is array value" 83 | $hashValue = @() 84 | if ($value[0] -is [hashtable] -or $value[0] -is [System.Collections.Specialized.OrderedDictionary] -or $value[0] -is [PSCustomObject]) 85 | { 86 | foreach ($item in $value) 87 | { 88 | $hashValue += ($item | ConvertTo-Hash) 89 | } 90 | } 91 | else 92 | { 93 | $hashValue = $value 94 | } 95 | $value = $hashValue 96 | } 97 | $hash.Add($name,$value) 98 | } 99 | } 100 | $hash 101 | } 102 | } -------------------------------------------------------------------------------- /Functions/Update-ARMresourceList.ps1: -------------------------------------------------------------------------------- 1 | #Requires -Version 5.0 2 | function Update-ARMresourceList 3 | { 4 | <# 5 | .SYNOPSIS 6 | This will update the allResources.json file that is used as input when creating a New-ARMresource 7 | 8 | .DESCRIPTION 9 | This will update the allResources.json file that is used as input when creating a New-ARMresource. This cmdlet supports 10 | ShouldProcess (whatif). 11 | 12 | .PARAMETER Credential 13 | Credentials for an Azure subscription 14 | 15 | .PARAMETER Force 16 | Force an update of the file if it exists 17 | 18 | .EXAMPLE 19 | Update-ARMresourceList 20 | 21 | This will try and fetch the resoruce provider list from Azure using the current AzureRMcontext. If no AzureRMcontext exists, it will 22 | invoke Login-AzureRMAccount without credentials. 23 | 24 | .EXAMPLE 25 | Update-ARMresourceList -Credential (Get-Credential -Message "Azure Credential") 26 | 27 | This will try and fetch the resoruce provider list from Azure using the current AzureRMcontext. If no AzureRMcontext exists, it will 28 | invoke Login-AzureRMAccount with the specified credentials. 29 | 30 | .INPUTS 31 | PSCustomObject 32 | 33 | .OUTPUTS 34 | string 35 | 36 | .NOTES 37 | Author: Tore Groneng 38 | Website: www.firstpoint.no 39 | Twitter: @ToreGroneng 40 | #> 41 | [cmdletbinding( 42 | SupportsShouldProcess=$true 43 | )] 44 | Param( 45 | [pscredential]$Credential 46 | , 47 | [switch]$Force 48 | ) 49 | $LoggedIn = $false 50 | $f = $MyInvocation.InvocationName 51 | Write-Verbose -Message "$f - START" 52 | 53 | try 54 | { 55 | Write-Verbose -Message "$f - Getting AzureRMContext" 56 | 57 | Get-AzureRmContext 58 | $LoggedIn = $true 59 | } 60 | catch 61 | { 62 | $ex = $_.Exception 63 | Write-Verbose -Message "$f - Get-AzureRmContext threw an exception, propably not loggedin" 64 | } 65 | 66 | if ($LoggedIn -eq $false) 67 | { 68 | if ($Credential) 69 | { 70 | Write-Verbose -Message "$f - Invoking Login-AzureRmAccount with credentials" 71 | Login-AzureRmAccount -Credential $Credential -ErrorAction Stop 72 | } 73 | else 74 | { 75 | Write-Verbose -Message "$f - Invoking Login-AzureRmAccount without credentials" 76 | Login-AzureRmAccount -ErrorAction Stop 77 | } 78 | } 79 | 80 | $fileName = Split-Path -Path $PSScriptRoot -Parent | Join-Path -ChildPath Data | Join-Path -ChildPath "allResources.json" 81 | 82 | $outFile = @{} 83 | $shouldProcessOperation = "Creating file" 84 | 85 | if ($Force.IsPresent) 86 | { 87 | $outFile.Add("Force", $true) 88 | $shouldProcessOperation = "Overwriting file" 89 | } 90 | 91 | if ($pscmdlet.ShouldProcess("$fileName", $shouldProcessOperation)) 92 | { 93 | Write-Verbose -Message "$f - Getting a providerlist, saving to [$fileName]" 94 | Get-AzureRmResourceProvider -ListAvailable | ConvertTo-Json -Depth 10 | Out-File -FilePath "$fileName" -Encoding utf8 @outFile 95 | } 96 | Write-Verbose -Message "$f - END" 97 | } -------------------------------------------------------------------------------- /Functions/New-ARMfunction.ps1: -------------------------------------------------------------------------------- 1 | function New-ARMfunction 2 | { 3 | [cmdletbinding()] 4 | Param( 5 | #[Parameter(ParameterSetName='concat')] 6 | [switch]$ConCat 7 | , 8 | #[Parameter(ParameterSetName='concat')] 9 | [string[]]$Values 10 | ) 11 | DynamicParam 12 | { 13 | $Dictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary 14 | 15 | $NewDynParam = @{ 16 | Name = "Variable" 17 | Mandatory = $false 18 | ValueFromPipelineByPropertyName = $true 19 | ValueFromPipeline = $false 20 | DPDictionary = $Dictionary 21 | } 22 | 23 | #ParameterSetName = "__AllParameterSets" 24 | #ParameterSetName = "ByVariable" 25 | 26 | $allVars = $script:Template.variables.Keys 27 | 28 | if ($allVars) 29 | { 30 | $null = $NewDynParam.Add("ValidateSet",$allVars) 31 | } 32 | else 33 | { 34 | $null = $NewDynParam.Add("ValidateSet",@("-")) 35 | } 36 | 37 | New-DynamicParam @NewDynParam 38 | 39 | $NewDynParam.Name = "Parameter" 40 | #$NewDynParam.ParameterSetName = "ByParam" 41 | $allParams = $script:Template.parameters.Keys 42 | 43 | if (($allParams | Measure-Object).Count -eq 1) 44 | { 45 | $allParams = @(,$allParams) 46 | } 47 | 48 | if ($allParams) 49 | { 50 | $NewDynParam.ValidateSet = $allParams 51 | } 52 | else 53 | { 54 | $NewDynParam.ValidateSet = @("empty","box") 55 | } 56 | 57 | New-DynamicParam @NewDynParam 58 | 59 | #$Dictionary | ConvertTo-Json | out-file -FilePath c:\temp\dyn.json -Encoding utf8 -Append 60 | 61 | $Dictionary 62 | } 63 | 64 | Begin 65 | { 66 | $f = $MyInvocation.InvocationName 67 | throw "$f is not implemented" 68 | } 69 | 70 | Process 71 | { 72 | $var = $PSBoundParameters.Variable 73 | $param = $PSBoundParameters.Parameter 74 | 75 | $hashKeyIndex = [ordered]@{} 76 | $index = 0 77 | foreach ($key in $PSBoundParameters.Keys) 78 | { 79 | $hashKeyIndex.Add($key,$index) 80 | $index++ 81 | } 82 | 83 | if ($ConCat) 84 | { 85 | $ConcatValue = "[concat('" 86 | if ($Values) 87 | { 88 | return "$ConcatValue$($Values -join "','")))]" 89 | } 90 | 91 | if ($var -and $param) 92 | { 93 | $ConcatValue = "[concat(" 94 | $varValue = "variable('$var')" 95 | $paramValue = "parameter('$param')" 96 | 97 | $paramIndex = $hashKeyIndex.Parameter 98 | $varIndex = $hashKeyIndex.Variable 99 | 100 | if ($paramIndex -lt $varIndex) 101 | { 102 | return "$ConcatValue" + "$paramValue," + "$varValue" + ")]" 103 | } 104 | 105 | if ($varIndex -lt $paramIndex) 106 | { 107 | return "$ConcatValue" + "$varValue," + "$paramValue" + "')]" 108 | } 109 | } 110 | } 111 | } 112 | } -------------------------------------------------------------------------------- /Functions/New-ARMresource.ps1: -------------------------------------------------------------------------------- 1 | #Requires -Version 5.0 2 | function New-ARMresource 3 | { 4 | <# 5 | .SYNOPSIS 6 | Create a new ARM template resource 7 | 8 | .DESCRIPTION 9 | Create a new ARM template resource 10 | 11 | .PARAMETER Name 12 | The name of the parameter. This is Mandatory 13 | 14 | .PARAMETER Type 15 | The parameter type. These are the types returned by Get-ARMresourceList 16 | 17 | .EXAMPLE 18 | $newRes = @{ 19 | APIversion = '2016-03-30' 20 | Name = 'MyVM' 21 | Location = 'EAST-US' 22 | Tags = @{tag=1} 23 | Comments = 'hey' 24 | DependsOn = @("item1","item2") 25 | SKU = @{value="skuvalue"} 26 | Kind = 'storage' 27 | Properties = @{prop1=1} 28 | } 29 | 30 | New-ARMresource @newRes -Type Microsoft.Compute/virtualMachines 31 | 32 | .INPUTS 33 | String 34 | 35 | .OUTPUTS 36 | PSCustomObject 37 | 38 | .NOTES 39 | Author: Tore Groneng 40 | Website: www.firstpoint.no 41 | Twitter: @ToreGroneng 42 | #> 43 | [cmdletbinding()] 44 | Param( 45 | [string] 46 | $APIversion 47 | , 48 | [string] 49 | $Name 50 | , 51 | [string] 52 | $Location 53 | , 54 | [hashtable] 55 | $Tags 56 | , 57 | [string] 58 | $Comments 59 | , 60 | [string[]] 61 | $DependsOn 62 | , 63 | [hashtable] 64 | $SKU 65 | , 66 | [string] 67 | $Kind 68 | , 69 | [hashtable] 70 | $Properties 71 | , 72 | [array] 73 | $Resources 74 | ) 75 | DynamicParam 76 | { 77 | $Dictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary 78 | 79 | $NewDynParam = @{ 80 | Name = "Type" 81 | Alias = "ResourceName" 82 | Mandatory = $true 83 | ValueFromPipelineByPropertyName = $true 84 | ValueFromPipeline = $true 85 | DPDictionary = $Dictionary 86 | } 87 | 88 | $all = Get-ARMresourceList -ErrorAction SilentlyContinue 89 | 90 | if ($all) 91 | { 92 | $null = $NewDynParam.Add("ValidateSet",$all) 93 | } 94 | 95 | New-DynamicParam @NewDynParam 96 | $Dictionary 97 | } 98 | 99 | Begin 100 | { 101 | $f = $MyInvocation.InvocationName 102 | Write-Verbose -Message "$f - START" 103 | } 104 | 105 | Process 106 | { 107 | $ResourceName = $PSBoundParameters.Type 108 | 109 | $propHash = [ordered]@{ 110 | PSTypeName = "ARMresource" 111 | apiVersion = $APIversion 112 | type = $ResourceName 113 | } 114 | 115 | if ($Name) 116 | { 117 | $propHash["name"] = $Name 118 | } 119 | 120 | if ($Location) 121 | { 122 | $propHash.location = $Location 123 | } 124 | 125 | if ($DependsOn) 126 | { 127 | $propHash.dependsOn = $DependsOn 128 | } 129 | 130 | if ($Properties) 131 | { 132 | $propHash.properties = $Properties 133 | } 134 | 135 | if ($Tags) 136 | { 137 | $propHash.tags = $Tags 138 | } 139 | 140 | if ($Comments) 141 | { 142 | $propHash.comments = $Comments 143 | } 144 | 145 | if ($Resources) 146 | { 147 | $propHash.resources = $Resources 148 | } 149 | 150 | if ($SKU) 151 | { 152 | $propHash.SKU = $SKU 153 | } 154 | 155 | if ($Kind) 156 | { 157 | $propHash.kind = $Kind 158 | } 159 | 160 | [PSCustomObject]$propHash 161 | } 162 | } -------------------------------------------------------------------------------- /Tests/New-ARMparameter.Tests.ps1: -------------------------------------------------------------------------------- 1 | #$here = Split-Path -Parent $MyInvocation.MyCommand.Path 2 | #$sut = (Split-Path -Leaf $MyInvocation.MyCommand.Path) -replace '\.Tests\.', '.' 3 | #. "$here\$sut" 4 | 5 | $modulePath = Split-Path $PSScriptRoot -Parent 6 | $modulepath = Join-Path -Path $modulePath -ChildPath posharm.psd1 7 | Import-Module $modulePath 8 | 9 | Describe "New-ARMparameter" { 10 | 11 | $ExpectedParm = @{ 12 | Name = 'Resource' 13 | Type = 'string' 14 | DefaultValue = 'meh' 15 | AllowedValues = @("meh", "det","foo") 16 | MinValue = 1 17 | MaxValue = 3 18 | MinLength = '3' 19 | MaxLength = '99' 20 | Description = 'Description' 21 | Metadata = @{Comment="yalla"} 22 | } 23 | 24 | $actualParameter = New-ARMparameter @ExpectedParm 25 | 26 | Context "Create object" { 27 | 28 | $actual = $actualParameter.($ExpectedParm.Name) 29 | 30 | It "Should create a new parameter object" { 31 | $actualParameter | Should not Be $null 32 | } 33 | 34 | It "Should be of type [PScustomObject]" { 35 | $actualParameter.GetType().Name | Should be "PScustomObject" 36 | } 37 | 38 | It "Should create a PSCustomObject with PStypeName 'ARMparameter'" { 39 | $actualParameter.pstypenames[0] | Should be "ARMparameter" 40 | } 41 | 42 | It "Should create a property with name [$($ExpectedParm.Name)]" { 43 | $actual | Should not be $null 44 | } 45 | 46 | it "Should have a type with value [$($ExpectedParm.Type)]" { 47 | $actual.Type | Should be $ExpectedParm.Type 48 | } 49 | 50 | It "Should have an DefaultValue of [$($ExpectedParm.DefaultValue)]" { 51 | $actual.DefaultValue | Should be $ExpectedParm.DefaultValue 52 | } 53 | 54 | $actualValue = $actual.AllowedValues -join "" 55 | $expectedValue = $ExpectedParm.AllowedValues -join "" 56 | 57 | It "Should have AllowedValues equal [$expectedValue]" { 58 | $actualValue | Should be $expectedValue 59 | } 60 | 61 | It "Should have an MinValue of [$($ExpectedParm.MinValue)]" { 62 | $actual.MinValue | Should be $ExpectedParm.MinValue 63 | } 64 | 65 | It "Should have an MaxValue [$($ExpectedParm.MaxValue)]" { 66 | $actual.MaxValue | Should be $ExpectedParm.MaxValue 67 | } 68 | 69 | It "Should have an MinLength [$($ExpectedParm.MinLength)]" { 70 | $actual.MinLength | Should be $ExpectedParm.MinLength 71 | } 72 | 73 | It "Should have an MaxLength [$($ExpectedParm.MaxLength)]" { 74 | $actual.MaxLength | Should be $ExpectedParm.MaxLength 75 | } 76 | 77 | $meta = $actual.MetaData 78 | 79 | It "Should have a MetaData property [$(($meta | Out-HashString) -replace [environment]::NewLine,'')]" { 80 | $meta | should not be $null 81 | } 82 | 83 | It "MetaData should be of type [PScustomObject]" { 84 | $meta.GetType().Name | Should be "PScustomObject" 85 | } 86 | 87 | It "MetaData should have an Comment property with value [$($ExpectedParm.Metadata.Comment)]" { 88 | $meta.Comment | Should be $ExpectedParm.Metadata.Comment 89 | } 90 | 91 | It "MetaData should have an Description property with value [$($ExpectedParm.Description)]" { 92 | $meta.Description | Should be $ExpectedParm.Description 93 | } 94 | } 95 | } 96 | 97 | Remove-Module -name posharm -ErrorAction SilentlyContinue -------------------------------------------------------------------------------- /Functions/Out-HashString.ps1: -------------------------------------------------------------------------------- 1 | function Out-HashString 2 | { 3 | <# 4 | .SYNOPSIS 5 | Convert an hashtable or and OrderedDictionary to a string 6 | 7 | .DESCRIPTION 8 | Convert an hashtable or and OrderedDictionary to a string 9 | 10 | .PARAMETER InputObject 11 | The object that is to be converted 12 | 13 | .PARAMETER PreSpacing 14 | Number of spaces used for indentation 15 | 16 | .EXAMPLE 17 | $hashObject = @{ 18 | Name = "Tore" 19 | Goal = "Rule the World" 20 | } 21 | $hashObject | Out-HashString 22 | 23 | This will convert the hashtable to the following string 24 | @{ 25 | Name = "Tore" 26 | Goal = "Rule the World" 27 | } 28 | 29 | .INPUTS 30 | Hashtable 31 | 32 | .OUTPUTS 33 | string 34 | 35 | .NOTES 36 | Author: Tore Groneng 37 | Website: www.firstpoint.no 38 | Twitter: @ToreGroneng 39 | #> 40 | [cmdletbinding()] 41 | Param( 42 | [Parameter(ValueFromPipeLine)] 43 | $InputObject 44 | , 45 | [string]$PreSpacing = " " 46 | ) 47 | Begin 48 | { 49 | $f = $MyInvocation.InvocationName 50 | Write-Verbose -Message "$f - START" 51 | $newLine = [environment]::NewLine 52 | 53 | if ($PreSpacing) 54 | { 55 | $endspaceCount = $PreSpacing.Length - 4 56 | if ($endspaceCount -lt 0){$endspaceCount = 0} 57 | $endspace = " " * $endspaceCount 58 | $beginSpace = " " * $endspaceCount 59 | } 60 | } 61 | Process 62 | { 63 | $out = "@{" 64 | 65 | $out = $out + $newLine 66 | $preSpace = $PreSpacing 67 | 68 | if (-not $InputObject -or $InputObject.keys.count -eq 0) {return "@{}"} 69 | 70 | foreach ($key in $InputObject.Keys) 71 | { 72 | Write-Verbose -Message "$f - Processing key [$key]" 73 | 74 | if ($key.Contains('$')) 75 | { 76 | $DisplayKey = "'$key'" 77 | } 78 | 79 | $value = $InputObject.$key 80 | $objType = $value.GetType().Name 81 | Write-Verbose -Message "$f - ObjectType = $objType" 82 | 83 | $mode = "stringValue" 84 | 85 | if ($objType -eq "Hashtable" -or $objType -eq "OrderedDictionary") 86 | { 87 | $mode = "hashtable" 88 | } 89 | 90 | if ($value -is [array]) 91 | { 92 | if ($value[0]) 93 | { 94 | $arrayType = $value[0].GetType().Name 95 | Write-Verbose -Message "$f - arrayType is [$arrayType]" 96 | 97 | if ($arrayType -eq "Hashtable" -or $arrayType -eq "OrderedDictionary") 98 | { 99 | $mode = "HashTableValue" 100 | } 101 | else 102 | { 103 | $mode = "ArrayValue" 104 | } 105 | } 106 | } 107 | 108 | Write-Verbose -Message "$f - Mode is [$mode]" 109 | if ($DisplayKey) 110 | { 111 | $key = $DisplayKey 112 | $DisplayKey = $null 113 | } 114 | 115 | $out += "$PreSpacing$key = " 116 | 117 | switch ($mode) 118 | { 119 | 'stringValue' 120 | { 121 | $out += '"' + $value + '"' + $newLine 122 | } 123 | 124 | 'hashtable' 125 | { 126 | $stringValue = Out-HashString -InputObject $value -PreSpacing "$PreSpacing " 127 | $out += $stringValue + $newLine 128 | } 129 | 130 | 'HashTableValue' 131 | { 132 | $stringValue = "" 133 | foreach ($arrayHash in $value) 134 | { 135 | $hashString = Out-HashString -InputObject $arrayHash -PreSpacing "$PreSpacing " 136 | $hash = " $hashString" 137 | $hash = "$hash," + $newLine 138 | $stringValue += $hash 139 | } 140 | $separatorIndex = $stringValue.LastIndexOf(",") 141 | $stringValue = $stringValue.Remove($separatorIndex,1) 142 | $out += $stringValue 143 | } 144 | 145 | 'ArrayValue' 146 | { 147 | $out += '"' +($value -join '","') + '"' + $newLine 148 | } 149 | 150 | Default {} 151 | } 152 | } 153 | $out += "$endspace}" 154 | $out 155 | } 156 | } -------------------------------------------------------------------------------- /PoshARM.psd1: -------------------------------------------------------------------------------- 1 | # 2 | # Module manifest for module 'PoshARM' 3 | # 4 | # Generated by: tore 5 | # 6 | # Generated on: 28.12.2016 7 | # 8 | 9 | @{ 10 | 11 | # Script module or binary module file associated with this manifest. 12 | RootModule = 'PoshARM.psm1' 13 | 14 | # Version number of this module. 15 | ModuleVersion = '0.1.0.0' 16 | 17 | # Supported PSEditions 18 | # CompatiblePSEditions = @() 19 | 20 | # ID used to uniquely identify this module 21 | GUID = '3153c88f-b648-4492-9ac3-a942d26c4903' 22 | 23 | # Author of this module 24 | Author = 'Tore Groneng' 25 | 26 | # Company or vendor of this module 27 | CompanyName = 'Firstpoint AS' 28 | 29 | # Copyright statement for this module 30 | Copyright = '(c) 2016 Tore Groneng. All rights reserved. ' 31 | 32 | # Description of the functionality provided by this module 33 | Description = 'A Powershell module for Azure ARM templates. Create templates with Powershell.' 34 | 35 | # Minimum version of the Windows PowerShell engine required by this module 36 | PowerShellVersion = '5.0' 37 | 38 | # Name of the Windows PowerShell host required by this module 39 | # PowerShellHostName = '' 40 | 41 | # Minimum version of the Windows PowerShell host required by this module 42 | # PowerShellHostVersion = '' 43 | 44 | # Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only. 45 | # DotNetFrameworkVersion = '' 46 | 47 | # Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only. 48 | # CLRVersion = '' 49 | 50 | # Processor architecture (None, X86, Amd64) required by this module 51 | # ProcessorArchitecture = '' 52 | 53 | # Modules that must be imported into the global environment prior to importing this module 54 | # RequiredModules = @() 55 | 56 | # Assemblies that must be loaded prior to importing this module 57 | # RequiredAssemblies = @() 58 | 59 | # Script files (.ps1) that are run in the caller's environment prior to importing this module. 60 | # ScriptsToProcess = @() 61 | 62 | # Type files (.ps1xml) to be loaded when importing this module 63 | # TypesToProcess = @() 64 | 65 | # Format files (.ps1xml) to be loaded when importing this module 66 | # FormatsToProcess = @() 67 | 68 | # Modules to import as nested modules of the module specified in RootModule/ModuleToProcess 69 | # NestedModules = @() 70 | 71 | # 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. 72 | FunctionsToExport = 'Add-ARMparameter','Add-ARMresource','Add-ARMvariable','ConvertTo-Hash','Get-ARMparameter','Get-ARMparameterScript','Get-ARMresourceList','Get-ARMresourceScript','Get-ARMtemplate','Get-ARMtemplateScript','Get-ARMvariable','Get-ARMvariableScript','Get-FunctionList','Import-ARMtemplate','New-ARMfunction','New-ARMparameter','New-ARMresource','New-ARMTemplate','New-ARMvariable','New-DynamicParam','Out-HashString','Set-ARMmetadata','Set-ARMparameter','Set-ARMvariable','Update-ARMresourceList' 73 | 74 | # 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. 75 | CmdletsToExport = '*' 76 | 77 | # Variables to export from this module 78 | VariablesToExport = '*' 79 | 80 | # 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. 81 | AliasesToExport = @() 82 | 83 | # DSC resources to export from this module 84 | # DscResourcesToExport = @() 85 | 86 | # List of all modules packaged with this module 87 | # ModuleList = @() 88 | 89 | # List of all files packaged with this module 90 | # FileList = @() 91 | 92 | # 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. 93 | PrivateData = @{ 94 | 95 | PSData = @{ 96 | 97 | # Tags applied to this module. These help with module discovery in online galleries. 98 | Tags = @('Azure','Template','ARM','JSON','Resource','Manager','Convert','Import') 99 | 100 | # A URL to the license for this module. 101 | # LicenseUri = '' 102 | 103 | # A URL to the main website for this project. 104 | ProjectUri = 'https://github.com/torgro/PoshARM' 105 | 106 | # A URL to an icon representing this module. 107 | # IconUri = '' 108 | 109 | # ReleaseNotes of this module 110 | # ReleaseNotes = '' 111 | 112 | } # End of PSData hashtable 113 | 114 | } # End of PrivateData hashtable 115 | 116 | # HelpInfo URI of this module 117 | # HelpInfoURI = '' 118 | 119 | # Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix. 120 | # DefaultCommandPrefix = '' 121 | 122 | } 123 | 124 | -------------------------------------------------------------------------------- /Tests/Syntax.Tests.ps1: -------------------------------------------------------------------------------- 1 | $here = Split-Path -Parent $MyInvocation.MyCommand.Path | Split-Path -Parent | Join-Path -ChildPath Functions 2 | 3 | function Measure-String 4 | { 5 | [cmdletbinding()] 6 | Param( 7 | [Parameter(ValueFromPipeline)] 8 | [string[]]$InputObject 9 | , 10 | [string]$SearchString 11 | , 12 | [switch]$SkipComments 13 | ) 14 | 15 | Begin 16 | { 17 | $count = 0 18 | $inCommentSection = $false 19 | $PreviousStr = "" 20 | } 21 | 22 | Process 23 | { 24 | if ($SearchString.Length -gt 1) 25 | { 26 | foreach ($str in $InputObject) 27 | { 28 | if($SkipComments.IsPresent) 29 | { 30 | if ($Str.Contains("<#")) 31 | { 32 | $inCommentSection = $true 33 | Write-Verbose "in commentssection" 34 | } 35 | 36 | $trimmed = $str.TrimStart() 37 | if ($PreviousStr.Contains("#>")) 38 | { 39 | Write-Verbose "end of commentssection" 40 | $inCommentSection = $false 41 | } 42 | 43 | $PreviousStr = $str 44 | 45 | if ($trimmed.StartsWith("#") -or $inCommentSection -eq $true) 46 | { 47 | continue 48 | } 49 | } 50 | 51 | Write-Verbose "Searching [$str] for [$SearchString]" 52 | $stringlength = $str.length 53 | $searchTextLength = $SearchString.Length 54 | $replaced = $str.ToLower().Replace($SearchString.ToLower(),"") 55 | $foundCount = ($stringlength - $replaced.length) / $searchTextLength 56 | $count += $foundCount 57 | } 58 | } 59 | else 60 | { 61 | foreach ($str in $InputObject) 62 | { 63 | 64 | if($SkipComments.IsPresent) 65 | { 66 | if ($Str.Contains("<#")) 67 | { 68 | $inCommentSection = $true 69 | Write-Verbose "Start of comment ssection" 70 | } 71 | 72 | $trimmed = $str.TrimStart() 73 | if ($PreviousStr.Contains("#>")) 74 | { 75 | Write-Verbose "End of comment ssection" 76 | $inCommentSection = $false 77 | } 78 | 79 | $PreviousStr = $str 80 | 81 | if ($trimmed.StartsWith("#") -or $inCommentSection -eq $true) 82 | { 83 | Write-Verbose "Skipping searching [$str] for [$SearchString]" 84 | continue 85 | } 86 | } 87 | 88 | Write-Verbose "Searching [$str] for [$SearchString]" 89 | foreach ($char in $str.ToCharArray()) 90 | { 91 | if ($char -eq $SearchString) 92 | { 93 | $count++ 94 | } 95 | } 96 | } 97 | } 98 | } 99 | END 100 | { 101 | [Pscustomobject]@{ 102 | SearchString = $SearchString 103 | Count = $count 104 | } 105 | } 106 | } 107 | 108 | 109 | Describe "Powershell Syntax Tests" { 110 | $files = Get-ChildItem -Path $here | Where-Object Name -notlike "*.Tests.ps1" 111 | #$files = Get-ChildItem -Path $here | Where-Object Name -eq "New-DynamicParam.ps1" 112 | 113 | foreach ($file in $files) 114 | { 115 | $content = Get-Content -Path $file.fullname -Encoding UTF8 -ReadCount 0 -Raw 116 | $fileName = $file.FileName 117 | $name = $file.BaseName.Replace(".Tests","") 118 | $functionNameCount = Measure-String -InputObject $content -SearchString $name | Select-Object -ExpandProperty Count 119 | $quotes = Measure-String -InputObject $content -SearchString "'" -SkipComments | Select-Object -ExpandProperty Count 120 | $doubleQuotes = Measure-String -InputObject $content -SearchString '"' | Select-Object -ExpandProperty Count 121 | $doubleDollar = Measure-String -InputObject $content -SearchString '$$' | Select-Object -ExpandProperty Count 122 | $ifFormatting = Measure-String -InputObject $content -SearchString "if(" | Select-Object -ExpandProperty Count 123 | $foreachFormatting = Measure-String -InputObject $content -SearchString "foreach(" | Select-Object -ExpandProperty Count 124 | $cmdletbindingCount = Measure-String -InputObject $content -SearchString "cmdletbinding(" | Select-Object -ExpandProperty Count 125 | $aliasExceptions = @("foreach","h","r","type") 126 | $aliases = Get-Alias | Where Name -notin $aliasExceptions | Select-Object -ExpandProperty Name 127 | $fixmeCount = Measure-String -InputObject $content -SearchString "FIXme" | Select-Object -ExpandProperty Count 128 | 129 | foreach ($alias in $aliases) 130 | { 131 | $aliasCount = (Measure-String -InputObject $content -SearchString " $alias " -SkipComments).Count 132 | if ($aliasCount -ne 0) 133 | { 134 | It "[$name] should not use Alias [$alias]" { 135 | $aliasCount | Should Be 0 136 | } 137 | } 138 | } 139 | 140 | It "[$name] should not be null" { 141 | $content | Should Not Be $null 142 | } 143 | 144 | It "[$name] should match function Name" { 145 | $functionNameCount | Should Not Be 0 146 | } 147 | 148 | It "[$name] Quotes count should be even" { 149 | $quotes % 2 | Should Be 0 150 | } 151 | 152 | It "[$name] doubleQuotes count should be even" { 153 | $doubleQuotes % 2 | Should Be 0 154 | } 155 | 156 | It "[$name] double dollar count should be 0" { 157 | $doubleDollar | Should Be 0 158 | } 159 | 160 | It "[$name] should not have if() formatting" { 161 | $ifFormatting | Should Be 0 162 | } 163 | 164 | It "[$name] should not have foreach() formatting" { 165 | $foreachFormatting | Should Be 0 166 | } 167 | 168 | It "[$name] should have cmdletbinding specified" { 169 | $cmdletbindingCount | should BeGreaterThan 0 170 | } 171 | 172 | It "[$name] should not have [FIXme] comment" { 173 | $fixmeCount | should be 0 174 | } 175 | 176 | $aliasCount = 0 177 | } 178 | } -------------------------------------------------------------------------------- /TestFiles/SimpleVM.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 | ], 34 | "metadata": { 35 | "description": "The Windows version for the VM. This will pick a fully patched image of this given Windows version." 36 | } 37 | } 38 | }, 39 | "variables": { 40 | "storageAccountName": "[concat(uniquestring(resourceGroup().id), 'sawinvm')]", 41 | "nicName": "myVMNic", 42 | "addressPrefix": "10.0.0.0/16", 43 | "subnetName": "Subnet", 44 | "subnetPrefix": "10.0.0.0/24", 45 | "publicIPAddressName": "myPublicIP", 46 | "vmName": "SimpleWinVM", 47 | "virtualNetworkName": "MyVNET", 48 | "subnetRef": "[resourceId('Microsoft.Network/virtualNetworks/subnets', variables('virtualNetworkName'), variables('subnetName'))]" 49 | }, 50 | "resources": [ 51 | { 52 | "type": "Microsoft.Storage/storageAccounts", 53 | "name": "[variables('storageAccountName')]", 54 | "apiVersion": "2016-01-01", 55 | "location": "[resourceGroup().location]", 56 | "sku": { 57 | "name": "Standard_LRS" 58 | }, 59 | "kind": "Storage", 60 | "properties": {} 61 | }, 62 | { 63 | "apiVersion": "2016-03-30", 64 | "type": "Microsoft.Network/publicIPAddresses", 65 | "name": "[variables('publicIPAddressName')]", 66 | "location": "[resourceGroup().location]", 67 | "properties": { 68 | "publicIPAllocationMethod": "Dynamic", 69 | "dnsSettings": { 70 | "domainNameLabel": "[parameters('dnsLabelPrefix')]" 71 | } 72 | } 73 | }, 74 | { 75 | "apiVersion": "2016-03-30", 76 | "type": "Microsoft.Network/virtualNetworks", 77 | "name": "[variables('virtualNetworkName')]", 78 | "location": "[resourceGroup().location]", 79 | "properties": { 80 | "addressSpace": { 81 | "addressPrefixes": [ 82 | "[variables('addressPrefix')]" 83 | ] 84 | }, 85 | "subnets": [ 86 | { 87 | "name": "[variables('subnetName')]", 88 | "properties": { 89 | "addressPrefix": "[variables('subnetPrefix')]" 90 | } 91 | } 92 | ] 93 | } 94 | }, 95 | { 96 | "apiVersion": "2016-03-30", 97 | "type": "Microsoft.Network/networkInterfaces", 98 | "name": "[variables('nicName')]", 99 | "location": "[resourceGroup().location]", 100 | "dependsOn": [ 101 | "[resourceId('Microsoft.Network/publicIPAddresses/', variables('publicIPAddressName'))]", 102 | "[resourceId('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]" 103 | ], 104 | "properties": { 105 | "ipConfigurations": [ 106 | { 107 | "name": "ipconfig1", 108 | "properties": { 109 | "privateIPAllocationMethod": "Dynamic", 110 | "publicIPAddress": { 111 | "id": "[resourceId('Microsoft.Network/publicIPAddresses',variables('publicIPAddressName'))]" 112 | }, 113 | "subnet": { 114 | "id": "[variables('subnetRef')]" 115 | } 116 | } 117 | } 118 | ] 119 | } 120 | }, 121 | { 122 | "apiVersion": "2015-06-15", 123 | "type": "Microsoft.Compute/virtualMachines", 124 | "name": "[variables('vmName')]", 125 | "location": "[resourceGroup().location]", 126 | "dependsOn": [ 127 | "[resourceId('Microsoft.Storage/storageAccounts/', variables('storageAccountName'))]", 128 | "[resourceId('Microsoft.Network/networkInterfaces/', variables('nicName'))]" 129 | ], 130 | "properties": { 131 | "hardwareProfile": { 132 | "vmSize": "Standard_A2" 133 | }, 134 | "osProfile": { 135 | "computerName": "[variables('vmName')]", 136 | "adminUsername": "[parameters('adminUsername')]", 137 | "adminPassword": "[parameters('adminPassword')]" 138 | }, 139 | "storageProfile": { 140 | "imageReference": { 141 | "publisher": "MicrosoftWindowsServer", 142 | "offer": "WindowsServer", 143 | "sku": "[parameters('windowsOSVersion')]", 144 | "version": "latest" 145 | }, 146 | "osDisk": { 147 | "name": "osdisk", 148 | "vhd": { 149 | "uri": "[concat(reference(resourceId('Microsoft.Storage/storageAccounts/', variables('storageAccountName'))).primaryEndpoints.blob, 'vhds/osdisk.vhd')]" 150 | }, 151 | "caching": "ReadWrite", 152 | "createOption": "FromImage" 153 | }, 154 | "dataDisks": [ 155 | { 156 | "name": "datadisk1", 157 | "diskSizeGB": "100", 158 | "lun": 0, 159 | "vhd": { 160 | "uri": "[concat(reference(resourceId('Microsoft.Storage/storageAccounts/', variables('storageAccountName'))).primaryEndpoints.blob, 'vhds/datadisk1.vhd')]" 161 | }, 162 | "createOption": "Empty" 163 | } 164 | ] 165 | }, 166 | "networkProfile": { 167 | "networkInterfaces": [ 168 | { 169 | "id": "[resourceId('Microsoft.Network/networkInterfaces',variables('nicName'))]" 170 | } 171 | ] 172 | }, 173 | "diagnosticsProfile": { 174 | "bootDiagnostics": { 175 | "enabled": "true", 176 | "storageUri": "[reference(resourceId('Microsoft.Storage/storageAccounts/', variables('storageAccountName'))).primaryEndpoints.blob]" 177 | } 178 | } 179 | } 180 | } 181 | ], 182 | "outputs": { 183 | "hostname": { 184 | "type": "string", 185 | "value": "[reference(variables('publicIPAddressName')).dnsSettings.fqdn]" 186 | } 187 | } 188 | } -------------------------------------------------------------------------------- /Tests/ConvertTo-Hash.Tests.ps1: -------------------------------------------------------------------------------- 1 | #$here = Split-Path -Parent $MyInvocation.MyCommand.Path 2 | #$sut = (Split-Path -Leaf $MyInvocation.MyCommand.Path) -replace '\.Tests\.', '.' 3 | #. "$here\$sut" 4 | 5 | #$path = "C:\Users\tore\Dropbox\SourceTreeRepros\PoshARM\PoshARM.psd1" 6 | #Import-Module $path 7 | 8 | $modulePath = Split-Path $PSScriptRoot -Parent 9 | $modulepath = Join-Path -Path $modulePath -ChildPath posharm.psd1 10 | Import-Module $modulePath 11 | 12 | Describe "ConvertTo-Hash" { 13 | 14 | $simpleObject = [pscustomobject]@{ 15 | Name = "Tore" 16 | Age = 45 17 | } 18 | 19 | $nestedObject = [pscustomobject]@{ 20 | Name = "Tore" 21 | Address = @{ 22 | Street = "TimesSquare 1" 23 | City = "New York" 24 | } 25 | } 26 | 27 | Context "Without pipeline" { 28 | $hash = ConvertTo-Hash -InputObject $simpleObject 29 | $nestedHash = ConvertTo-Hash -InputObject $nestedObject 30 | 31 | It "Should be a OrderedDictionary object type" { 32 | $hash.GetType().Name | should be "OrderedDictionary" 33 | } 34 | 35 | It "Should have a Name Key" { 36 | $hash.Name | Should be $simpleObject.Name 37 | } 38 | 39 | It "Should have a Age Key" { 40 | $hash.Age | Should be $simpleObject.Age 41 | } 42 | 43 | It "Should create a nested hashtable" { 44 | $nestedHash.Address | should not be $null 45 | } 46 | 47 | It "Should create a hashtable object" { 48 | $nestedHash.Address.GetType().Name | Should be "hashtable" 49 | } 50 | 51 | It "Should have a street address [$($nestedObject.Address.Street)]" { 52 | $nestedHash.Address.Street | should be $nestedObject.Address.Street 53 | } 54 | 55 | It "Should have a city address [$($nestedObject.Address.City)]" { 56 | $nestedHash.Address.City | should be $nestedObject.Address.City 57 | } 58 | 59 | 60 | } 61 | 62 | Context "With pipeline" { 63 | $hash = $null 64 | $nestedHash = $null 65 | $hash = $simpleObject | ConvertTo-Hash 66 | $nestedHash = $nestedObject | ConvertTo-Hash 67 | 68 | It "Should be a OrderedDictionary object type" { 69 | $hash.GetType().Name | should be "OrderedDictionary" 70 | } 71 | 72 | It "Should have a Name Key" { 73 | $hash.Name | Should be $simpleObject.Name 74 | } 75 | 76 | It "Should have a Age Key" { 77 | $hash.Age | Should be $simpleObject.Age 78 | } 79 | 80 | It "Should create a nested hashtable" { 81 | $nestedHash.Address | should not be $null 82 | } 83 | 84 | It "Should create a hashtable object" { 85 | $nestedHash.Address.GetType().Name | Should be "hashtable" 86 | } 87 | 88 | It "Should have a street address [$($nestedObject.Address.Street)]" { 89 | $nestedHash.Address.Street | should be $nestedObject.Address.Street 90 | } 91 | 92 | It "Should have a city address [$($nestedObject.Address.City)]" { 93 | $nestedHash.Address.City | should be $nestedObject.Address.City 94 | } 95 | } 96 | 97 | Context "Array handling with pipeline" { 98 | 99 | $array = @() 100 | foreach ($int in (1..2)) 101 | { 102 | $array += [PSCustomObject]@{ 103 | Number = $int 104 | Name = "My name is $int" 105 | } 106 | } 107 | $arrayObj = [pscustomobject]@{ArrayItem = $array} 108 | $assert = $arrayObj | ConvertTo-Hash 109 | 110 | It "arrayObj should be a PSCustomObject" { 111 | $arrayObj.GetType().Name | Should be "PSCustomObject" 112 | } 113 | 114 | It "Value of ArrayItem should be of type Array" { 115 | $arrayObj.ArrayItem -is [array] | Should be $true 116 | } 117 | 118 | it "Should convert the array to a collection of OrderedDictionary"{ 119 | $assert.GetType().Name | Should be "OrderedDictionary" 120 | } 121 | 122 | $index = 0 123 | foreach ($hashItem in $assert.ArrayItem) 124 | { 125 | It "Should have a collection of OrderedDictionary values" { 126 | $hashItem.GetType().Name | Should Be "OrderedDictionary" 127 | } 128 | 129 | It "Should have Number key with value [$($arrayObj.ArrayItem[$index].Number)]" { 130 | $hashItem.Number | Should be $arrayObj.ArrayItem[$index].Number 131 | } 132 | $index++ 133 | } 134 | } 135 | 136 | Context "Array handling without pipeline" { 137 | 138 | $array = @() 139 | foreach ($int in (1..2)) 140 | { 141 | $array += [PSCustomObject]@{ 142 | Number = $int 143 | Name = "My name is $int" 144 | } 145 | } 146 | $arrayObj = [pscustomobject]@{ArrayItem = $array} 147 | $assert = ConvertTo-Hash -InputObject $arrayObj 148 | 149 | It "arrayObj should be a PSCustomObject" { 150 | $arrayObj.GetType().Name | Should be "PSCustomObject" 151 | } 152 | 153 | It "Value of ArrayItem should be of type Array" { 154 | $arrayObj.ArrayItem -is [array] | Should be $true 155 | } 156 | 157 | it "Should convert the array to a collection of OrderedDictionary"{ 158 | $assert.GetType().Name | Should be "OrderedDictionary" 159 | } 160 | 161 | $index = 0 162 | foreach ($hashItem in $assert.ArrayItem) 163 | { 164 | It "Should have a collection of OrderedDictionary values" { 165 | $hashItem.GetType().Name | Should Be "OrderedDictionary" 166 | } 167 | 168 | It "Should have Number key with value [$($arrayObj.ArrayItem[$index].Number)]" { 169 | $hashItem.Number | Should be $arrayObj.ArrayItem[$index].Number 170 | } 171 | $index++ 172 | } 173 | } 174 | 175 | Context "Passthrou Hashtable with pipeline" { 176 | $hash = @{test=1} 177 | $assert = $hash | ConvertTo-Hash 178 | It "Should Passthrou inputobject if it is a hashtable" { 179 | $assert.GetType().Name | Should be "hashtable" 180 | } 181 | 182 | It "Should have a test key with value [1]" { 183 | $assert.test | should be 1 184 | } 185 | } 186 | 187 | Context "Passthrou OrderedDictionary with pipeline" { 188 | $ordered = [ordered]@{test=1} 189 | $assert = $ordered | ConvertTo-Hash 190 | 191 | It "Should Passthrou inputobject if it is a OrderedDictionary" { 192 | $assert.GetType().Name | Should be "OrderedDictionary" 193 | } 194 | 195 | It "Should have a test key with value [1]" { 196 | $assert.test | should be 1 197 | } 198 | } 199 | 200 | Context "Passthrou Hashtable without pipeline" { 201 | $hash = @{test=1} 202 | $assert = ConvertTo-Hash -InputObject $hash 203 | 204 | It "Should Passthrou inputobject if it is a hashtable" { 205 | $assert.GetType().Name | Should be "hashtable" 206 | } 207 | 208 | It "Should have a test key with value [1]" { 209 | $assert.test | should be 1 210 | } 211 | } 212 | 213 | Context "Passthrou OrderedDictionary without pipeline" { 214 | $ordered = [ordered]@{test=1} 215 | $assert = ConvertTo-Hash -InputObject $ordered 216 | 217 | It "Should Passthrou inputobject if it is a OrderedDictionary" { 218 | $assert.GetType().Name | Should be "OrderedDictionary" 219 | } 220 | 221 | It "Should have a test key with value [1]" { 222 | $assert.test | should be 1 223 | } 224 | } 225 | } 226 | 227 | Remove-Module -name posharm -ErrorAction SilentlyContinue -------------------------------------------------------------------------------- /Functions/New-DynamicParam.ps1: -------------------------------------------------------------------------------- 1 | function New-DynamicParam 2 | { 3 | [cmdletbinding()] 4 | <# 5 | .SYNOPSIS 6 | Helper function to simplify creating dynamic parameters 7 | 8 | .DESCRIPTION 9 | Helper function to simplify creating dynamic parameters 10 | 11 | Example use cases: 12 | Include parameters only if your environment dictates it 13 | Include parameters depending on the value of a user-specified parameter 14 | Provide tab completion and intellisense for parameters, depending on the environment 15 | 16 | Please keep in mind that all dynamic parameters you create will not have corresponding variables created. 17 | One of the examples illustrates a generic method for populating appropriate variables from dynamic parameters 18 | Alternatively, manually reference $PSBoundParameters for the dynamic parameter value 19 | 20 | .NOTES 21 | Credit to http://jrich523.wordpress.com/2013/05/30/powershell-simple-way-to-add-dynamic-parameters-to-advanced-function/ 22 | Added logic to make option set optional 23 | Added logic to add RuntimeDefinedParameter to existing DPDictionary 24 | Added a little comment based help 25 | Credit to https://github.com/RamblingCookieMonster/PowerShell/blob/master/New-DynamicParam.ps1 26 | 27 | Credit to BM for alias and type parameters and their handling 28 | 29 | .PARAMETER Name 30 | Name of the dynamic parameter 31 | 32 | .PARAMETER Type 33 | Type for the dynamic parameter. Default is string 34 | 35 | .PARAMETER Alias 36 | If specified, one or more aliases to assign to the dynamic parameter 37 | 38 | .PARAMETER ValidateSet 39 | If specified, set the ValidateSet attribute of this dynamic parameter 40 | 41 | .PARAMETER Mandatory 42 | If specified, set the Mandatory attribute for this dynamic parameter 43 | 44 | .PARAMETER ParameterSetName 45 | If specified, set the ParameterSet attribute for this dynamic parameter 46 | 47 | .PARAMETER Position 48 | If specified, set the Position attribute for this dynamic parameter 49 | 50 | .PARAMETER ValueFromPipelineByPropertyName 51 | If specified, set the ValueFromPipelineByPropertyName attribute for this dynamic parameter 52 | 53 | .PARAMETER HelpMessage 54 | If specified, set the HelpMessage for this dynamic parameter 55 | 56 | .PARAMETER DPDictionary 57 | If specified, add resulting RuntimeDefinedParameter to an existing RuntimeDefinedParameterDictionary (appropriate for multiple dynamic parameters) 58 | If not specified, create and return a RuntimeDefinedParameterDictionary (appropriate for a single dynamic parameter) 59 | 60 | See final example for illustration 61 | 62 | .EXAMPLE 63 | 64 | function Show-Free 65 | { 66 | [CmdletBinding()] 67 | Param() 68 | DynamicParam { 69 | $options = @( Get-WmiObject win32_volume | foreach {$_.driveletter} | sort-object ) 70 | New-DynamicParam -Name Drive -ValidateSet $options -Position 0 -Mandatory 71 | } 72 | begin{ 73 | #have to manually populate 74 | $drive = $PSBoundParameters.drive 75 | } 76 | process{ 77 | $vol = Get-WmiObject win32_volume -Filter "driveletter='$drive'" 78 | "{0:N2}% free on {1}" -f ($vol.Capacity / $vol.FreeSpace),$drive 79 | } 80 | } #Show-Free 81 | 82 | Show-Free -Drive 83 | 84 | # This example illustrates the use of New-DynamicParam to create a single dynamic parameter 85 | # The Drive parameter ValidateSet populates with all available volumes on the computer for handy tab completion / intellisense 86 | 87 | .EXAMPLE 88 | 89 | # I found many cases where I needed to add more than one dynamic parameter 90 | # The DPDictionary parameter lets you specify an existing dictionary 91 | # The block of code in the Begin block loops through bound parameters and defines variables if they don't exist 92 | 93 | Function Test-DynPar{ 94 | [cmdletbinding()] 95 | param( 96 | [string[]]$x = $Null 97 | ) 98 | DynamicParam 99 | { 100 | #Create the RuntimeDefinedParameterDictionary 101 | $Dictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary 102 | 103 | New-DynamicParam -Name AlwaysParam -ValidateSet @( Get-WmiObject win32_volume | foreach {$_.driveletter} | sort-object ) -DPDictionary $Dictionary 104 | 105 | #Add dynamic parameters to $dictionary 106 | if ($x -eq 1) 107 | { 108 | New-DynamicParam -Name X1Param1 -ValidateSet 1,2 -mandatory -DPDictionary $Dictionary 109 | New-DynamicParam -Name X1Param2 -DPDictionary $Dictionary 110 | New-DynamicParam -Name X3Param3 -DPDictionary $Dictionary -Type DateTime 111 | } 112 | else 113 | { 114 | New-DynamicParam -Name OtherParam1 -Mandatory -DPDictionary $Dictionary 115 | New-DynamicParam -Name OtherParam2 -DPDictionary $Dictionary 116 | New-DynamicParam -Name OtherParam3 -DPDictionary $Dictionary -Type DateTime 117 | } 118 | 119 | #return RuntimeDefinedParameterDictionary 120 | $Dictionary 121 | } 122 | Begin 123 | { 124 | #This standard block of code loops through bound parameters... 125 | #If no corresponding variable exists, one is created 126 | #Get common parameters, pick out bound parameters not in that set 127 | Function _temp { [cmdletbinding()] param() } 128 | $BoundKeys = $PSBoundParameters.keys | Where-Object { (get-command _temp | select-object -ExpandProperty parameters).Keys -notcontains $_} 129 | foreach ($param in $BoundKeys) 130 | { 131 | if (-not ( Get-Variable -name $param -scope 0 -ErrorAction SilentlyContinue ) ) 132 | { 133 | New-Variable -Name $Param -Value $PSBoundParameters.$param 134 | Write-Verbose "Adding variable for dynamic parameter '$param' with value '$($PSBoundParameters.$param)'" 135 | } 136 | } 137 | 138 | #Appropriate variables should now be defined and accessible 139 | Get-Variable -scope 0 140 | } 141 | } 142 | 143 | # This example illustrates the creation of many dynamic parameters using New-DynamicParam 144 | # You must create a RuntimeDefinedParameterDictionary object ($dictionary here) 145 | # To each New-DynamicParam call, add the -DPDictionary parameter pointing to this RuntimeDefinedParameterDictionary 146 | # At the end of the DynamicParam block, return the RuntimeDefinedParameterDictionary 147 | # Initialize all bound parameters using the provided block or similar code 148 | 149 | .FUNCTIONALITY 150 | PowerShell Language 151 | 152 | #> 153 | param( 154 | [string]$Name 155 | , 156 | [System.Type]$Type = [string] 157 | , 158 | [string]$TypeAsString 159 | , 160 | [string[]]$Alias = @() 161 | , 162 | [string[]]$ValidateSet 163 | , 164 | [scriptblock]$validateScript 165 | , 166 | [switch]$Mandatory 167 | , 168 | [string]$ParameterSetName = "__AllParameterSets" 169 | , 170 | [int]$Position 171 | , 172 | [switch]$ValueFromPipelineByPropertyName 173 | , 174 | [switch]$ValueFromPipeline 175 | , 176 | [string]$HelpMessage 177 | , 178 | [validatescript({ 179 | if (-not ( $_ -is [System.Management.Automation.RuntimeDefinedParameterDictionary] -or -not $_) ) 180 | { 181 | Throw "DPDictionary must be a System.Management.Automation.RuntimeDefinedParameterDictionary object, or not exist" 182 | } 183 | $True 184 | })] 185 | $DPDictionary = $false 186 | 187 | ) 188 | Add-Type @" 189 | public class DynParamQuotedString { 190 | 191 | public DynParamQuotedString(string quotedString) : this(quotedString, "'") {} 192 | public DynParamQuotedString(string quotedString, string quoteCharacter) { 193 | OriginalString = quotedString; 194 | _quoteCharacter = quoteCharacter; 195 | } 196 | //' 197 | public string OriginalString { get; set; } 198 | string _quoteCharacter; 199 | 200 | public override string ToString() { 201 | if (OriginalString.Contains(" ")) { 202 | return string.Format("{1}{0}{1}", OriginalString, _quoteCharacter); 203 | } 204 | else { 205 | return OriginalString; 206 | } 207 | } 208 | } 209 | "@ 210 | if ($PSBoundParameters.ContainsKey("TypeAsString")) 211 | { 212 | $type = [System.Type]$TypeAsString 213 | } 214 | #Create attribute object, add attributes, add to collection 215 | $ParamAttr = New-Object System.Management.Automation.ParameterAttribute 216 | $ParamAttr.ParameterSetName = $ParameterSetName 217 | 218 | if ($mandatory) 219 | { 220 | $ParamAttr.Mandatory = $True 221 | } 222 | if ($Position -ne $null) 223 | { 224 | $ParamAttr.Position=$Position 225 | } 226 | if ($ValueFromPipelineByPropertyName) 227 | { 228 | $ParamAttr.ValueFromPipelineByPropertyName = $True 229 | } 230 | if ($ValueFromPipeline) 231 | { 232 | $ParamAttr.ValueFromPipeline = $True 233 | } 234 | if ($HelpMessage) 235 | { 236 | $ParamAttr.HelpMessage = $HelpMessage 237 | } 238 | 239 | $AttributeCollection = New-Object 'Collections.ObjectModel.Collection[System.Attribute]' 240 | $AttributeCollection.Add($ParamAttr) 241 | 242 | #param validation set if specified 243 | if ($ValidateSet) 244 | { 245 | $ParamOptions = New-Object System.Management.Automation.ValidateSetAttribute -ArgumentList $ValidateSet 246 | $AttributeCollection.Add($ParamOptions) 247 | } 248 | 249 | if ($validateScript) 250 | { 251 | $paramScript = New-Object -TypeName System.Management.Automation.ValidateScriptAttribute -ArgumentList $validateScript 252 | $AttributeCollection.Add($paramScript) 253 | } 254 | 255 | #Aliases if specified 256 | if ($Alias.count -gt 0) { 257 | $ParamAlias = New-Object System.Management.Automation.AliasAttribute -ArgumentList $Alias 258 | $AttributeCollection.Add($ParamAlias) 259 | } 260 | 261 | 262 | #Create the dynamic parameter 263 | $Parameter = New-Object -TypeName System.Management.Automation.RuntimeDefinedParameter -ArgumentList @($Name, $Type, $AttributeCollection) 264 | 265 | #Add the dynamic parameter to an existing dynamic parameter dictionary, or create the dictionary and add it 266 | if ($DPDictionary) 267 | { 268 | $DPDictionary.Add($Name, $Parameter) 269 | } 270 | else 271 | { 272 | $Dictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary 273 | $Dictionary.Add($Name, $Parameter) 274 | $Dictionary 275 | } 276 | } --------------------------------------------------------------------------------