The intent in Bicep is that one creates fully idempotent templates so that you should receive the same output every time you attempt to deploy anything in Bicep. As such, randomString and newGuid both accept parameters you can use to seed the result, but you'll always get the same result for the new values you put in.
For the reasons above, you're encouraged to provide your own generated password to kick off template deployment externally so there's nothing about the Bicep template that's changing from one deployment to another. On top of that, you're not really encouraged to expose the password in an output value since those are all logged indefinitely, so I highly recommend writing out the value to a Key Vault secret as in the following
@secure() //Prevents it from being logged, but also removes it from output
param password string = newGuid() //Can only be used as the default value for a param
@description('The name of the Key Vault to save the secret to')
param KeyVaultName string
@description('The name of the secret in Key Vault')
param KeyVaultSecretName string
//Save as Key Vault secret
resource KeyVault 'Microsoft.KeyVault/vaults@2021-06-01-preview' existing = {
name: KeyVaultName
}
resource KVSecret 'Microsoft.KeyVault/vaults/secrets@2021-06-01-preview' = {
name: replace(replace(SecretName, '.', '-'), ' ', '-')
parent: KeyVault
properties: {
contentType: 'text/plain'
attributes: {
enabled: true
}
value: password
}
}
output PasswordSecretUri string = KVSecret.properties.secretUri
And then you can use the GUID output as your password for your use-case knowing that it will be different every time you run the Bicep deployment.
Edit:
If you simply want a more complex password, I suggest using a PowerShell deployment script to generate a password that matches your complexity requirements. Here's a small snippet that can do just that, adapted from here to include a shuffle function:
function Shuffle-String([string]$inputString) {
$charArray = $inputString.ToCharArray()
$rng = New-Object System.Random
$remainingChars = $charArray.length
while ($remainingChars -gt 1) {
$remainingChars--
$charIndex = $rng.Next($remainingChars + 1)
$value = $charArray[$charIndex]
$charArray[$charIndex] = $charArray[$remainingChars]
$charArray[$remainingChars] = $value
}
return -join $charArray
}
function Create-Password() {
$TokenSet = @{
U = [Char[]]'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
L = [Char[]]'abcdefghijklmnopqrstuvwxyz'
N = [Char[]]'0123456789'
S = [Char[]]'!"#$%&''()*+,-./:;<=>?@[\]^_`{|}~'
}
$Upper = Get-Random -Count 5 -InputObject $TokenSet.U
$Lower = Get-Random -Count 5 -InputObject $TokenSet.L
$Number = Get-Random -Count 5 -InputObject $TokenSet.N
$Special = Get-Random -Count 5 -InputObject $TokenSet.S
$Combined = ($Upper + $Lower + $Number + $Special) -join ''
return Shuffle-String $Combined
}
Call with 'Create-Password' to get an output like 'WT{"v3)ziD21H48Lw_q'.
For completeness, here's what your deployment script module would then look like:
param Location string = resourceGroup().location
param UtcNow string = utcNow()
param DeploymentScriptName string = newGuid()
var ScriptContent = '''
function Shuffle-String([string]$inputString) {
$charArray = $inputString.ToCharArray()
$rng = New-Object System.Random
$remainingChars = $charArray.length
while ($remainingChars -gt 1) {
$remainingChars--
$charIndex = $rng.Next($remainingChars + 1)
$value = $charArray[$charIndex]
$charArray[$charIndex] = $charArray[$remainingChars]
$charArray[$remainingChars] = $value
}
return -join $charArray
}
function Create-Password() {
$TokenSet = @{
U = [Char[]]'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
L = [Char[]]'abcdefghijklmnopqrstuvwxyz'
N = [Char[]]'0123456789'
S = [Char[]]'!"#$%&''()*+,-./:;<=>?@[\]^_`{|}~'
}
$Upper = Get-Random -Count 5 -InputObject $TokenSet.U
$Lower = Get-Random -Count 5 -InputObject $TokenSet.L
$Number = Get-Random -Count 5 -InputObject $TokenSet.N
$Special = Get-Random -Count 5 -InputObject $TokenSet.S
$Combined = ($Upper + $Lower + $Number + $Special) -join ''
return Shuffle-String $Combined
}
$DeploymentScriptOutputs = @{}
$DeploymentScriptOutputs['password'] = Create-Password
'''
resource DeploymentScript 'Microsoft.Resources/deploymentScripts@2020-10-01' = {
name: DeploymentScriptName
kind: 'AzurePowerShell'
location: Location
identity: {
}
properties: {
forceUpdateTag: UtcNow
azPowerShellVersion: '6.4'
scriptContent: ScriptContent
timeout: 'PT15M'
cleanupPreference: 'Always'
retentionInterval: 'PT1H'
arguments: ''
}
}
And as illustrated above, I recommend that you take the password value from the script output (in variable DeploymentScript.properties.outputs.password
) and pass it directly into module (e.g. from within this module as opposed to passing it in an output parameter to a parent module that subsequently calls another) that will save it as a secret in Azure Key Vault, applying the @secure()
attribute to the password parameter so it isn't logged.
Two important takeaways:
- Azure Powershell deployment scripts run Azure Powershell which is equivalent to Powershell 5.1. As such, you cannot use more recent shuffle functions like
$Combined | Get-Random -Shuffle
as they're not available.
- You don't mention a requirement for a cryptographically secure password, but let me address the lengthy thread under my post. I'm not aware of a cmdlet built into Powershell that is capable of generating cryptographically secure strings. Yes, locally, you could invoke the functionality from .NET via something like 'System.Security.Cryptography.RandomNumberGenerator', but a Bicep deployment script runs as a containerized instance without elevated privileges, so unless they happen to have the .NET SDK available in the container (which hasn't been my own experience), you're not going to be able to achieve this level of password-generating security.