Announcing the preview of PSArm

Steve Lee

Announcing PSArm preview

Today, we are pleased to announce the first preview of a new experimental module that make it easier than ever for PowerShell users to create Azure Resource Manager (ARM) templates: PSArm.

This module enables existing PowerShell users to author ARM templates using PowerShell syntax.

Similar to Azure Bicep, PSArm is an independent module that creates the necessary ARM JSON template to deploy and configure Azure infrastructure in a PowerShell context. PSArm allows PowerShell users who are familiar with ARM to write complex deployment templates by mixing the declarative syntax of ARM with the imperative syntax of PowerShell.

With PSArm, users can take advantage of PowerShell features when authoring ARM templates, such as:

  • Variables, loops, conditionals, and pipelines
  • Completion support
  • Object-orientation, with an object model for ARM templates
  • Parameter validation

The PSArm module is still in early preview and is intended both as a demonstration both of how PowerShell can be leveraged to make ARM authoring easier and as a proof of concept for domain-specific languages (DSL) in PowerShell. As a preview release, there may be breaking changes as development continues based on user feedback.

Bicep and PSArm

Bicep is Azure’s primary, supported Domain Specific Language (DSL) for deploying Azure resources declaratively. It aims to drastically simplify the ARM template authoring experience with a cleaner syntax, improved type safety, and better support for modularity and code re-use.

PSArm is different from Bicep in that it is built for users who are already familiar and comfortable with PowerShell and its syntax.

Additionally, Bicep is almost entirely a declarative language–meaning it represents a declaration of the end-state of Azure resources–while PSArm exposes the capabilities of PowerShell scripting alongside ARM resource declarations.

We would also like to thank the Bicep team for their help and feedback as we developed PSArm.  In fact, the template schema support in PSArm comes from the bicep-types-az project, which also powers Bicep.

How can I start using this module?

PSArm has been published to the PowerShell Gallery. The module can also be built from source by following the instructions on the PSArm GitHub repository.

Once you have the module installed you can begin authoring ARM templates with PowerShell. To use PSArm, use the Arm keyword followed by a scriptblock. From within this scriptblock, PSArm provides contextual functionality. Note that the file name must end with the .psarm.ps1 extension, for example newStorageAccount.psarm.ps1.

The following example builds an ARM template to create a new storage account:

param(
    [Parameter(Mandatory)]
    [string]
    $storageAccountName,

    [Parameter(Mandatory)]
    [string]
    $location,

    [Parameter()]
    [string]
    $accountType = 'Standard_LRS',

    [Parameter()]
    [string]
    $kind = 'StorageV2',

    [Parameter()]
    [string]
    $accessTier = 'Hot',

    [Parameter()]
    [string]
    $minimumTLSVersion = 'TLS1_2',

    [Parameter()]
    [bool]
    $supportsHTTPSTrafficOnly = 1,

    [Parameter()]
    [bool]
    $allowBlobPublicAccess = 1,

    [Parameter()]
    [bool]
    $allowSharedKeyAccess = 1
)

Arm {

    Resource $storageAccountName -Namespace 'Microsoft.Storage' -Type 'storageAccounts' -apiVersion '2019-06-01' -kind $kind -Location $location {
        ArmSku $accountType
        Properties {
            accessTier $accessTier
            minimumTLSVersion $minimumTLSVersion
            supportsHTTPSTrafficOnly $supportsHTTPSTrafficOnly
            allowBlobPublicAccess $allowBlobPublicAccess
            allowSharedKeyAccess $allowSharedKeyAccess
        }
    }
}

Because PSArm scripts are just ordinary PowerShell scripts, you can use a param block at the top of a .psarm.ps1 script as always to declare parameters, give them default values, mark them as mandatory, apply validation attributes like [ValidateSet(...)], and so on.

ARM template properties like resources and outputs are available in PSArm via keywords like Resource and Output. These keywords instantiate one resource or output instance at a time. These keywords can be used in any order.

Within each resource are resource-level keywords specific to that resource type, which enable you to specify attributes available to that resource. For resources with properties, a properties keyword specifies a block in which each resource property can be configured with its corresponding PowerShell keyword. These keywords can be discovered with PowerShell completions.

While using PSArm, PowerShell variables can be used removing the need for many ARM variable expressions, but there are a few cases where the ARM expressions may be necessary: – When a built-in ARM function needs to evaluated at deployment time, as in resourceGroup() or utcNow() – A variable is needed to be evaluated only once, as in uniqueString() to provide a hash value – When a secure input is required, as in the securestring parameter

PSArm also provides ARM built-in expression functions as their own keywords. These functions allow users to use PowerShell syntax to express function application and member access.

For more a more detailed explanation of the features and mechanisms available in PSArm, take a look at the README on the PSArm GitHub repository.

With the above PSArm script saved to newStorageAccount.psarm.ps1, you can turn it into an ARM JSON template with the Publish-PSArmTemplate cmdlet:

Publish-PSArmTemplate -Path .\newStorageAccount.psarm.ps1 -Parameters @{
    storageAccountName = 'storageName'
    location = 'location'
}

This will create the equivalent ARM template in .\template.json. This file can then be deployed like any other ARM template, for example using Azure PowerShell:

New-AzResourceGroupDeployment -ResourceGroupName myRG -TemplateFile .\template.json

The resulting ARM template can be viewed below:

{
  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "metadata": {
    "_generator": {
      "name": "psarm",
      "version": "0.1.0.0",
      "psarm-psversion": "7.1.0",
      "templateHash": "15035725580591843956"
    }
  },
  "resources": [
    {
      "name": "test",
      "type": "Microsoft.Resources/deployments",
      "apiVersion": "2019-10-01",
      "properties": {
        "mode": "Incremental",
        "expressionEvaluationOptions": {
          "scope": "inner"
        },
        "template": {
          "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
          "contentVersion": "1.0.0.0",
          "resources": [
            {
              "name": "psarmtest",
              "apiVersion": "2019-06-01",
              "type": "Microsoft.Storage/storageAccounts",
              "kind": "StorageV2",
              "location": "westus2",
              "sku": {
                "name": "Standard_LRS"
              },
              "properties": {
                "accessTier": "Hot",
                "minimumTlsVersion": "TLS1_2",
                "supportsHttpsTrafficOnly": true,
                "allowBlobPublicAccess": true,
                "allowSharedKeyAccess": true
              }
            }
          ]
        }
      }
    }
  ]
}

For more PSArm examples, see the examples folder in the PSArm repository.

Feedback and Support

Community feedback is essential to the development of PSArm and future experimentation. Feedback specifically on the conversion cmdlets, ConvertFrom-ArmTemplate and ConvertTo-PSArm, is appreciated. While there are no committed dates going forward, this is a quality driven project. If significant changes come in based off of user feedback, a release will occur to verify the issues have been addressed. To file an issue or to contribute, please visit the PSArm GitHub repository.

7 comments

Discussion is closed. Login to edit/delete existing comments.

    • Steve LeeMicrosoft employee 0

      Part of the intent of the PSArm project was to determine if we could develop a way to add DSLs to PowerShell w/o the need to make any engine changes. This would make it easier to support a broader set of customers who are using different versions of PowerShell. To be fair, modules like Pester are already a DSL, but the main thing that was missing is tabcompletion support. Rob, from the PowerShel team, found a novel way to solve this problem and the plan is to have some libraries in the future for others to leverage for their own DSLs.

      • Georgi Ivanov 0

        Thanks for the explanation Steve Lea, can you point me to some guide how to properly use the dynamicKeyword. I gave it a try but it was not working constantly when the dsl is implemented as script module.

        • Steve LeeMicrosoft employee 0

          DynamicKeyword is really only used by DSC. PSArm and Pester, for example, use functions as keywords. This will allow your DSL to work with older versions of PowerShell as well. You can take a look at the PSArm source https://github.com/powershell/psarm or Pester source https://github.com/pester/Pester.

  • Tomasz Wiśniewski 0

    Having Bicep in mind why this exists? What’s its purpose and what’s the relationship going to be with Bicep?

    • Steve LeeMicrosoft employee 0

      There is a section above that talks about Bicep and PSArm. The way to think about it is if you’re not an existing PowerShell user, then you should look at using Bicep. However, if you are already a PowerShell user, then PSArm may make more sense as it leverages your PowerShell experience and knowledge.

  • Chris Black | Azure MVP | Happy Azure Stacking!!! 0

    This is INCREDIBLE! Thank you so much for making this happen! I cannot wait to test it 🙂

Feedback usabilla icon