BicepDev.psm1
#Region '.\Public\Add-BDOutput.ps1' 0 function Add-BDOutput { <# .SYNOPSIS Add outputs to an arm template created by New-BDFile .DESCRIPTION Provide Name, Type, Value of the output to have it added. .EXAMPLE PS C:\> .\appGateway.json | Add-BDOutput -Name 'testString' -Type 'string' -value 'hello' Add a string output to appGateway.json .EXAMPLE PS C:\> $foo.BuiltModule | Add-BDOutput -Name 'testObject' -Type 'object' -value @{'hi'='hello'} Add an object output to appGateway.json .EXAMPLE PS C:\> $foo.BuiltModule | Add-BDOutput -Name 'testArray' -Type 'array' -value @('hi','hello') Add an array output to appGateway.json .INPUTS (New-BDFile).BuiltModule #> [CmdletBinding()] param ( [Parameter(Mandatory, ValueFromPipelineByPropertyName, HelpMessage = "The built bicep template returned from New-BDFile")] [System.IO.FileInfo] $BuiltModule, [parameter(Mandatory, HelpMessage = "Name of the output")] [string] $Name, [parameter(Mandatory, HelpMessage = "[type] of the output")] [string] $Type, [parameter(Mandatory, HelpMessage = "Value of the output")] $Value ) process { $ModuleFileContent = Get-Content $BuiltModule.FullName -Encoding 'utf8' -Raw | ConvertFrom-Json $ModuleFileContent.outputs | Add-Member NoteProperty $Name @{type = $type; value = $Value} -Force $ModuleFileContent | ConvertTo-Json -Depth 99 | Out-File $BuiltModule.FullName } } #EndRegion '.\Public\Add-BDOutput.ps1' 39 #Region '.\Public\Convert-BDParam.ps1' 0 function Convert-BDParam { <# .SYNOPSIS Converts parameters in an arm template to ouputs .DESCRIPTION Either convert all parameters, or those parameters specified in ParametersToConvert, to outputs .EXAMPLE PS C:\> New-BDFile -BicepDeploymentFile testAppGateway.bicep -BicepModuleFile appGateway.bicep -outvariable $BD PS C:\> $BD.BuiltModule | Convert-BDParam Convert all parameters in $BD.BuiltModule to outputs, with a prefix of 'pars_' as the name. .INPUTS (New-BDFile).BuiltModule #> [CmdletBinding()] param ( [Parameter(Mandatory, ValueFromPipelineByPropertyName, HelpMessage = "The built bicep template returned from New-BDFile")] [System.IO.FileInfo] $BuiltModule, [Parameter(HelpMessage = "Enter names of parameters to convert to outputs")] [alias('Params')] [string[]] $ParametersToConvert ) process { $ModuleFileContent = Get-Content $BuiltModule.FullName -Encoding 'utf8' -Raw | ConvertFrom-Json $ModuleFileContent.variables | Add-Member 'bdParams' ([pscustomobject]@{}) -Force if ($ParametersToConvert) { $Parameters = $ModuleFileContent.parameters.psobject.Properties | Where-Object {$_.Name -in $ParametersToConvert} Write-Verbose "Found $($Parameters.Count) of $($ParametersToConvert.Count) parameters to convert" if ($Parameters.Count -ne $ParametersToConvert.Count) { $MissingParams=(($ParametersToConvert | Where-Object {$_ -notin $ModuleFileContent.parameters.psobject.Properties.Name}) -join ', ') Write-Warning "Could not find these provided params: $MissingParams" } } else { $Parameters = $ModuleFileContent.parameters.psobject.Properties Write-Verbose "Found $($Parameters.Name.Count) parameters to convert" } $Parameters | ForEach-Object { $name = $_.name $value = "[parameters('{0}')]" -f $name $ModuleFileContent.variables.bdParams | Add-Member NoteProperty $name $value -Force } $ModuleFileContent.outputs | Add-Member NoteProperty "bdParams" @{type = 'object'; value = "[variables('bdParams')]" } -Force $ModuleFileContent | ConvertTo-Json -Depth 99 | Out-File $BuiltModule.FullName } } #EndRegion '.\Public\Convert-BDParam.ps1' 49 #Region '.\Public\Convert-BDVar.ps1' 0 function Convert-BDVar { <# .SYNOPSIS Converts all variables in an arm template to ouputs .DESCRIPTION This cmdlet adds one output to the $BuiltModule arm template: 'bdVars' When bicep builds to an ARM template, all copy loop array variables are added to a single variable named 'copy'. We use similar logic here to take all of those copy loops inside the copy var, and put them in a new ARM template variable, which we use to make a more simple output in bdVars. The pattern follows for all other variables. .EXAMPLE PS C:\> New-BDFile -BicepDeploymentFile testAppGateway.bicep -BicepModuleFile appGateway.bicep -outvariable $BD PS C:\> $BD.BuiltModule | Convert-BDVar Add all variables in $BD.BuiltModule to new variable (bdVars), and output variable bdVars in the arm template. .INPUTS (New-BDFile).BuiltModule .NOTES #> [CmdletBinding()] param ( [Parameter(Mandatory, ValueFromPipelineByPropertyName, HelpMessage = "The built bicep template returned from New-BDFile")] [System.IO.FileInfo] $BuiltModule, [Parameter(HelpMessage = "Enter names of variables to convert to outputs")] [alias('Vars')] [string[]] $VariablesToConvert ) process { $ModuleFileContent = Get-Content $BuiltModule.FullName -Encoding 'utf8' -Raw | ConvertFrom-Json $ModuleFileContent.variables | Add-Member 'bdVars' ([pscustomobject]@{}) -Force if ($VariablesToConvert) { foreach ($Variable in $VariablesToConvert) { #Check both top level variables and nested vars inside the 'copy' variable if ($ModuleFileContent.variables.psobject.properties.name -notcontains $Variable) { if ($ModuleFileContent.variables.copy.name -notcontains $Variable) { Write-Warning "Could not find a variable named $Variable" } else { $ModuleFileContent.variables.bdVars | Add-Member NoteProperty $Variable ("[variables('{0}')]" -f $Variable) -Force } } else { $ModuleFileContent.variables.bdVars | Add-Member NoteProperty $Variable $ModulefileContent.variables.$Variable -Force } } } else { $ModuleFileContent.variables.psobject.Properties | ForEach-Object { $name = $_.name $value = $_.value if ($name -notin @('copy','bdVars')) { $ModuleFileContent.variables.bdVars | Add-Member NoteProperty $name $PSItem.Value -Force } elseif ($name -eq 'copy') { foreach ($copyArray in $value) { $ModuleFileContent.variables.bdVars | Add-Member NoteProperty $copyArray.Name ("[variables('{0}')]" -f $copyArray.name) -Force } } } } $ModuleFileContent.outputs | Add-Member NoteProperty "bdVars" @{type = 'object'; value = "[variables('bdVars')]"} -Force $ModuleFileContent | ConvertTo-Json -Depth 99 | Out-File $BuiltModule.FullName } } #EndRegion '.\Public\Convert-BDVar.ps1' 68 #Region '.\Public\Get-BDOutput.ps1' 0 function Get-BDOutput { <# .SYNOPSIS Convert JObject, returned from 'Outputs' property of New-AzResourceGroupDeployment, to PSCustomObject. .DESCRIPTION This is a simple quality of life feature, since the output of New-AzResourceGroupDeployment isn't readily usable in PS .EXAMPLE PS C:\> New-BDFile -BicepDeploymentFile testAppGateway.bicep -BicepModuleFile appGateway.bicep -outvariable $BD PS C:\> New-AzResourceGroupDeployment -ResourceGroupName 'rg-bdfiles' -TemplateFile $BD.BuiltModule | Get-BDOutput -OutputName $BD.OutputName Grabs the appGateway output property and converts it from jqlinq to PSCustomObject .INPUTS [PSResourceGroupDeployment] .OUTPUTS [PsCustomObject] #> [CmdletBinding()] param ( [Parameter(Mandatory, ValueFromPipeline, HelpMessage = "The output of New-AzResourceGroupDeployment")] [Object] $InputObject, [Parameter(Mandatory, HelpMessage = "The name of the output variable to return")] [string] $OutputName ) process { $InputObject.outputs.$OutputName.Value.ToString() | ConvertFrom-Json } } #EndRegion '.\Public\Get-BDOutput.ps1' 30 #Region '.\Public\New-BDFile.ps1' 0 function New-BDFile { <# .SYNOPSIS Creates copies of bicep files to be used for testing with this module. .DESCRIPTION Given a bicep module that you want to develop/test with, and a bicep template file that calls that module, this cmdlet should copy both files, build your template, and output an ARM template ready for use by the other cmdlets in this module. .EXAMPLE PS C:\> New-BDFile -BicepDeploymentFile testAppGateway.bicep -BicepModuleFile appGateway.bicep -outvariable $BD PS C:\> $BD | fl BicepDeploymentFile : appGateway\ec03_test.bicep BicepModuleFile : appGateway\ec03_appGatewayMicrosoft.bicep BuiltModule : appGateway\ec03_appGatewayMicrosoft.json OutputProperty : appGateway Returns a PSObject of files to be used with the other cmdlets in the module .OUTPUTS [PSCustomObject] .NOTES TODO Write New-BDDeployment controller function #> [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory, HelpMessage = "The .bicep file that calls the bicep module we want to transform")] [ValidateScript( { Test-Path $_ })] [Alias('Deploy')] [string] $BicepDeploymentFile, [Parameter(Mandatory, HelpMessage = "The .bicep module we want to transform")] [ValidateScript( { Test-Path $_ })] [Alias('Module')] [string] $BicepModuleFile, [Parameter(HelpMessage = "Prevent this function from clearing out the resources array from the file we will be injecting output into")] [switch] $KeepResources ) #Create a new guid and copy the bicep files so we don't go mucking around in the original bicep files $GUID = (New-Guid).guid.split('-')[1] $NewBicDepName = "$($GUID)_" + (split-path $BicepDeploymentFile -leaf) $NewBicModName = "$($GUID)_" + (split-path $BicepModuleFile -leaf) $NewBicDepFile = Copy-Item -Path $BicepDeploymentFile -Destination (Join-Path (Split-Path $BicepDeploymentFile) $NewBicDepName) -PassThru -Force $NewBicModFile = Copy-Item -Path $BicepModuleFile -Destination (Join-Path (Split-Path $BicepModuleFile) $NewBicModName) -PassThru -Force #Replace path to $BicepModuleFile with $NewBicepModFile in $BicepDeploymentFile and build $ReplaceString = Split-Path $BicepModuleFile -Leaf $BicepDepFileContent = Get-Content $NewBicDepFile -Encoding 'utf8' -Raw $BicepDepFileContent = $BicepDepFileContent -replace "$ReplaceString\'.?=", "$($NewBicModFile.Name)' =" $BicepDepFileContent | Set-Content $NewBicDepFile -Force #If build was successful, update the path to $NewBicepModFile with the json file we built and return object for pipeline $BicepBuild = bicep build $($NewBicModFile.FullName) $BuiltModule = Get-Item ([Io.Path]::ChangeExtension($NewBicModFile.FullName, ".json")) -ErrorAction Ignore if ($BuiltModule) { $OutputName = (Get-Content $NewBicDepFile | Where-Object { $_ -match $NewBicModFile.Name }).Split(' ')[1] $NewBicepDepFileContent = (Get-Content $NewBicDepFile -Encoding 'utf8' -Raw) $NewBicepDepFileContent = $NewBicepDepFileContent -replace "'$($NewBicModFile.Name)'", "'$($BuiltModule.Name)'" $NewBicepDepFileContent += "`n output $OutputName object = $OutputName" $NewBicepDepFileContent | Set-Content $NewBicDepFile -Force if (-not $KeepResources) { $BuiltModuleContent = (Get-Content $BuiltModule -Encoding 'utf8' -Raw) | ConvertFrom-Json $BuiltModuleContent.resources = @() $BuiltModuleContent | ConvertTo-Json -Depth 99 | Set-Content $BuiltModule -Force } if (-not $BuiltModuleContent.outputs) { $BuiltModuleContent | Add-Member NoteProperty outputs ([PSCustomObject]@{}) -PassThru | ConvertTo-Json -Depth 99 | Set-Content $BuiltModule -Force } [PSCustomObject]@{ BicepDeploymentFile = $NewBicDepFile BicepModuleFile = $NewBicModFile BuiltModule = $BuiltModule OutputName = $OutputName } } #Remove BD files created if we didn't successfully build a module else { if (Test-Path $NewBicDepFile) { Remove-BDFile -BicepDeploymentFile $NewBicDepFile } if (Test-Path $NewBicModFile) { Remove-BDfile -BicepModuleFile $NewBicModFile } throw "Failed to build bicep file" } } #EndRegion '.\Public\New-BDFile.ps1' 89 #Region '.\Public\Remove-BDFile.ps1' 0 function Remove-BDFile { <# .SYNOPSIS Deletes files created by New-BDFile .DESCRIPTION Either delete all files from current session (Output of New-BDFile) or make a best effort at deleting all .json and .bicep files in the directory provided with RemoveFromDirectory .EXAMPLE PS C:\> New-BDFile -BicepDeploymentFile testAppGateway.bicep -BicepModuleFile appGateway.bicep -outvariable $BD PS C:\> $BD | Remove-BDFile Returns a PSObject of files to be used with the other cmdlets in the module .INPUTS (New-BDFile) .NOTES #> [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(ValueFromPipelineByPropertyName, HelpMessage = "The .bicep file that calls the bicep module we want to transform", ParameterSetName = 'pipe')] [Alias('Deploy')] [System.IO.FileInfo] $BicepDeploymentFile, [Parameter(ValueFromPipelineByPropertyName, HelpMessage = "The .bicep module we want to transform", ParameterSetName = 'pipe')] [Alias('Module')] [System.IO.FileInfo] $BicepModuleFile, [Parameter(ValueFromPipelineByPropertyName, HelpMessage = "The .json file that was built from New-BDFile", ParameterSetName = 'pipe')] [System.IO.FileInfo] $BuiltModule, [Parameter(HelpMessage = "Search for and remove files matching files created by New-BDFile", ParameterSetName = 'besteffort')] [ValidateScript({Test-Path $_})] [string] $RemoveFromDirectory ) process { if ($RemoveFromDirectory) { Write-Verbose "Searching for bicep and json files in $RemoveFromDirectory" Get-ChildItem -Path $RemoveFromDirectory -Recurse -File -Include *.bicep,*.json | Where-Object {$_.Name -match "^(\d|[a-z]){4}_"} | Foreach-Object { Write-Verbose "Attempting to remove $($_.FullName)" $_ | Remove-Item -Force } } else { $PSBoundParameters.GetEnumerator().ForEach( { Write-Verbose "Attempting to remove $($_.Value.Name)" $_.Value | Remove-Item -Force }) } } } #EndRegion '.\Public\Remove-BDFile.ps1' 50 |