Allow interpolation in Multiline strings
As per #416 the current implementation does not support inline string interpolation and the workarounds with format and concat have each their own draw-backs.
I'd be happy with the proposed solution of matching the number of $ to the number of quote marks.
Couldn't find an issue for this feature so here we go.
Need this when building large cloud config strings and injecting vars & params into them
the format workaround starts to fall apart when you have ~20 things you need to plug into the string
How about doing something python's f-strings or C#'s $-string interpolation:
var interpolated = 'interpolated'
var s = $'''
this is ${interpolated}'''
var ns = '''
this is not ${interpolated}'''
Agreed on this one. Running into this issue with APIM Policies.
Any update on this?
No update, but I didn't notice how many upvotes this had received. I'll move it to the committed backlog and put it up for discussion to make sure we are not conceptually against having this support in the language.
Right now, I use a workaround for Azure Data Explorer statements (it's very picky about what per-line values):
var mappingLine1 = '.create table ${TableName} ingestion json mapping "${MappingName}"'
var mappingLine2 = '''
'['
' {"column": "ColumnName", "Properties": {"Path": "$.columnName"}},'
']'
'''
var mappingLine = '${mappingLine1}\r\n${mappingLine2}'
It'd be so much cleaner (and could get rid of the environment-dependent newline characters) to have multi-line interpolation:
var mappingLine = $'''
.create table ${TableName} ingestion json mapping "${MappingName}"
'['
' {"column": "ColumnName", "Properties": {"Path": "$.columnName"}},'
']'
'''
For the record, I'd vote for the C# style of putting the $ in front of the string to indicate the interpolated values even if you stick with the Bicep approach of the $ in front of the curly braces within the string):
var message = "test";
var myString = $"This is a {test}";
On the community call today, someone asked for a link to the documentation detailing how this is done in C#. It's a new language feature as of C# 11 described here.
Looking back at @chriswue's comment:
How about doing something python's f-strings or C#'s $-string interpolation:
var interpolated = 'interpolated' var s = $''' this is ${interpolated}''' var ns = ''' this is not ${interpolated}'''
I think this syntax suggestion would win my vote, possibly with the addition of an arbitrary number of $ characters to permit unescaped ${ sequences:
var s = $$'''
this is $${interpolated}
this is not ${interpolated}'''
If we can close on a syntax, I think the implementation should actually be pretty straightforward - we should be able to adapt the implementations for verbatim strings & interpolated strings without too much difficulty.
@anthony-c-martin I think you do want to allow unescaped ${ sequences - otherwise writing shell script snippets could be come painful. So I like the multiple $ signs
Are there any updates for the prioritisation of this feature? This would be really useful to have when building out vm image templates
For people looking for a workaround, I had good results with the following code structure when dealing with custom script extensions:
commandToExecute: concat('powershell.exe -ExecutionPolicy Unrestricted -Command "',join([
'Start-Transcript \\"${installLog}\\"'
'Get-WindowsCapability -Online -Name open*'
'Add-WindowsCapability -Online -Name OpenSSH.Client~~~~0.0.1.0'
'Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0'
'Start-Service sshd'
'Set-Service -Name sshd -StartupType Automatic'
'$key = \'${sshkey}\''
'$key | Add-Content -Path \'C:\\ProgramData\\ssh\\administrators_authorized_keys\' -Encoding UTF8'
'icacls.exe \'C:\\ProgramData\\ssh\\administrators_authorized_keys\' /inheritance:r /grant \'Administrators:F\' /grant \'SYSTEM:F\''
'Stop-Transcript'],';'),
'"')
This code joins a bunch of powershell commands with ;. It is technically not a multi-line string but using join makes it readable since each command can go on a separate line on the template. It also allows for string interpolation as shown by ${installLog} and ${sshKey}.
If your use case is not PowerShell, you could have an array of strings and join by whichever character you require
This would be really helpful for dynamic where clauses in a complex KQL query in an alert, for example:
query: '''
let exceptionErrors = exceptions
| where severityLevel >= 3
| where customDimensions.MyCustomProp == "${MyDynamicValue}"
| project
TimeStamp = timestamp,
Message = strcat(
coalesce(outerMessage, ""),
iif(isnotempty(outerMessage) and isnotempty(innermostMessage), " → ", ""),
coalesce(innermostMessage, "")
),
ItemCount = itemCount,
App = cloud_RoleName,
Type = "Exception";
let traceErrors = traces
| where severityLevel >= 3
| where customDimensions.MyCustomProp == "${MyDynamicValue}"
| project
TimeStamp = timestamp,
Message = message,
ItemCount = itemCount,
App = cloud_RoleName,
Type = "Trace";
exceptionErrors
| union traceErrors
'''
Any news on this one?
I have a workaround for now for a KQL query, but would rather use string interpolation for multiline. The workaround I am using now:
var scheduledEventsBaseQuery = '''
arg("").containerserviceeventresources
| where type == "microsoft.containerservice/managedclusters/scheduledevents"'''
var scheduledEventsClusterIdQuery = '| where id contains "${resourceId('Microsoft.ContainerService/managedClusters', clusterName)}"'
var upgradeNotificationSelectQuery = '''
| where properties has "eventStatus"
| extend status = substring(properties, indexof(properties, "eventStatus") + strlen("eventStatus") + 3, 50)
| extend status = substring(status, 0, indexof(status, ",") - 1)
| where status != ""
| where properties has "eventDetails"
| extend upgradeType = case(
properties has "K8sVersionUpgrade",
"K8sVersionUpgrade",
properties has "NodeOSUpgrade",
"NodeOSUpgrade",
""
)
| extend details = parse_json(tostring(properties.eventDetails))
| where properties has "lastUpdateTime"
| extend eventTime = substring(properties, indexof(properties, "lastUpdateTime") + strlen("lastUpdateTime") + 3, 50)
| extend eventTime = substring(eventTime, 0, indexof(eventTime, ",") - 1)
| extend eventTime = todatetime(tostring(eventTime))
| where eventTime >= ago(2h)
| where upgradeType == "K8sVersionUpgrade"
| project
eventTime,
upgradeType,
status,
properties,
name,
details
| order by eventTime asc
'''
var upgradeNotificationQuery = '${scheduledEventsBaseQuery}\r\n${scheduledEventsClusterIdQuery}\r\n${upgradeNotificationSelectQuery}'