Skip to content

Commit

Permalink
ReAdd: readEnvironmentVariable function with default param (Azure#10849)
Browse files Browse the repository at this point in the history
This PR re-creates a new Bicep Function 'readEnviromentVariable' to
enable users to read environment variables to set bicep variables values
as earlier merged pr Azure#10726 got reverted. This Feature in only enabled
on the .BicepParams Files.

## Contributing a feature

* [X] I have opened a new issue for the proposal, or commented on an
existing one, and ensured that the Bicep maintainers are good with the
design of the feature being implemented.
Addresses setting default values for
#Azure#9576
* [X] I have included "Fixes #{issue_number}" in the PR description, so
GitHub can link to the issue and close it when the PR is merged
* [X] I have appropriate test coverage of my new feature
 
This PR extends the LoadEnvironmentVariable fucntion available for
.bicepparam files to have an optional argument to set a default value to
use if the environment variable did not exist.

### Signature
**function readEnvironmentVariable(variableName: string, [default:
string]): string**
Reads the specified Environment variable as bicep string. Variable
loading occurs during compilation, not at runtime.

user can set a default string value to be returned/used if the
environment variable does not exist.

### Syntax examples

```bicep
// read string value
 param stringfromEnvironmentVariables = readEnvironmentVariable('stringVariableName')

// read int value
 param intfromEnvironmentVariables = int(readEnvironmentVariable('intVariableName'))

//read a boolen value, and set default value to false if env variable not found
 param boolfromEnvironmentVariables = bool(readEnvironmentVariable('boolVariableName','false'))
```

### Code samples
min.bicep
``` bicep

@secure()
param administratorLoginPassword string

// SQL SERVER

// https://docs.microsoft.com/en-us/azure/templates/microsoft.sql/servers?tabs=bicep
resource sqlServer 'Microsoft.Sql/servers@2021-11-01' = {
  name: name
  location: location
  properties: {
    administratorLogin: administratorLogin
    administratorLoginPassword: administratorLoginPassword
    minimalTlsVersion: '1.2'
    version: '12.0'
  }
  tags:{
    GeneratedBy: 'Bicep_test'
    Units: string(counter)
  }
}

// https://docs.microsoft.com/en-us/azure/templates/microsoft.sql/servers/firewallrules?tabs=bicep
resource sqlFirewallRuleAzure 'Microsoft.Sql/servers/firewallRules@2021-11-01-preview' = {
  parent: sqlServer
  name: 'AzureAccess'
  properties: {
    endIpAddress: '0.0.0.0'
    startIpAddress: '0.0.0.0'
  }
}

output id string = sqlServer.id
output name string = sqlServer.name
output fqdn string = sqlServer.properties.fullyQualifiedDomainName
```

params.bicepparam

```bicep
using './main.bicep'

param name='mydb'
param location=toUpper('westus')

param administratorLogin=readEnvironmentVariable('AdminLogin')
param administratorLoginPassword= readEnvironmentVariable('AdminPassword')
param counter=int(readEnvironmentVariable('Counter','1'))
```

Set Env var values **AdminPassword=ABC , AdminLogin=user **. then
execute bicep build-param
```bash
bicep build-params, ./params.bicepparam  --outfile ./generatedparam.json --bicep-file./main.bicep
```

Expected behavior: output file generatedparam.json generated
successfully with values loaded form AdminPassword, and AdminLogin
values environment variables, while Counter value set from the default
vaue to 1
```json

{
  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "name": {
      "value": "mydb"
    },
    "location": {
      "value": "WESTUS"
    },
    "administratorLogin": {
      "value": "user"
    },
    "administratorLoginPassword": {
      "value": "ABC"
    },
    "counter": {
      "value": 1
    }
  }
}

```

Set Env var values **AdminPassword=ABC , AdminLogin=user , Counter=3**.
then execute bicep build-param

```bash
bicep build-params, ./params.bicepparam  --outfile ./generatedparam.json --bicep-file./main.bicep
```

Expected behavior: output file generatedparam.json generated
successfully with values loaded form evironment variables.
```json
{
  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "name": {
      "value": "mydb"
    },
    "location": {
      "value": "WESTUS"
    },
    "administratorLogin": {
      "value": "user"
    },
    "administratorLoginPassword": {
      "value": "ABC"
    },
    "counter": {
      "value": 3
    }
  }
}
```

Unset (AdminPassword , AdminLogin) environment Variables, then execute
bicep build-param :

Expected behavoiur: Build-param fail to generate the json output with
clear Error message

![image](https://github.com/Azure/bicep/assets/34171319/8f592365-9bb4-407d-91da-f687bfd9e9ff)

---------

Co-authored-by: Hattan Shobokshi <[email protected]>
  • Loading branch information
HadwaAbdelhalem and hattan committed Jun 1, 2023
1 parent 7848f47 commit b7859f8
Show file tree
Hide file tree
Showing 25 changed files with 636 additions and 372 deletions.
8 changes: 8 additions & 0 deletions src/Bicep.Cli.IntegrationTests/BuildParamsCommandTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -186,5 +186,13 @@ public async Task Build_Invalid_Single_Params_File_ShouldFail_WithExpectedErrorM
error.Should().ContainAll(diagnostics);
}
}

[TestInitialize]
public void testInit()
{
System.Environment.SetEnvironmentVariable("stringEnvVariableName", "test");
System.Environment.SetEnvironmentVariable("intEnvVariableName", "100");
System.Environment.SetEnvironmentVariable("boolEnvironmentVariable", "true");
}
}
}
8 changes: 8 additions & 0 deletions src/Bicep.Core.IntegrationTests/Emit/TemplateEmitterTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,14 @@ public void TemplateEmitter_should_not_dispose_text_writer()
emitter.Emit(stringWriter);
}

[TestInitialize]
public void testInit()
{
System.Environment.SetEnvironmentVariable("stringEnvVariableName", "test");
System.Environment.SetEnvironmentVariable("intEnvVariableName", "100");
System.Environment.SetEnvironmentVariable("boolEnvironmentVariable", "true");
}

private EmitResult EmitTemplate(SourceFileGrouping sourceFileGrouping, FeatureProviderOverrides features, string filePath)
{
var compilation = Services.WithFeatureOverrides(features).Build().BuildCompilation(sourceFileGrouping);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using Bicep.Core.Semantics.Namespaces;
using Bicep.Core.TypeSystem.Az;
using Bicep.Core.Features;
using Bicep.Core.Workspaces;
using System.Collections.Generic;
using System.Linq;

Expand All @@ -23,9 +24,9 @@ public TestExtensibilityNamespaceProvider(IAzResourceTypeLoader azResourceTypeLo
AadNamespaceType.BuiltInName,
});

public NamespaceType? TryGetNamespace(string providerName, string aliasName, ResourceScope resourceScope, IFeatureProvider featureProvider)
public NamespaceType? TryGetNamespace(string providerName, string aliasName, ResourceScope resourceScope, IFeatureProvider featureProvider, BicepSourceFileKind sourceFileKind)
{
if (defaultNamespaceProvider.TryGetNamespace(providerName, aliasName, resourceScope, featureProvider) is { } namespaceType)
if (defaultNamespaceProvider.TryGetNamespace(providerName, aliasName, resourceScope, featureProvider, sourceFileKind) is { } namespaceType)
{
return namespaceType;
}
Expand Down
5 changes: 3 additions & 2 deletions src/Bicep.Core.IntegrationTests/ImportTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
using Bicep.Core.UnitTests;
using Bicep.Core.UnitTests.Assertions;
using Bicep.Core.UnitTests.Utils;
using Bicep.Core.Workspaces;
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;

Expand All @@ -37,9 +38,9 @@ public TestNamespaceProvider(Dictionary<string, Func<string, NamespaceType>> bui

public IEnumerable<string> AvailableNamespaces => builderDict.Keys.Concat(new [] { SystemNamespaceType.BuiltInName });

public NamespaceType? TryGetNamespace(string providerName, string aliasName, ResourceScope resourceScope, IFeatureProvider features) => providerName switch
public NamespaceType? TryGetNamespace(string providerName, string aliasName, ResourceScope resourceScope, IFeatureProvider features, BicepSourceFileKind sourceFileKind) => providerName switch
{
SystemNamespaceType.BuiltInName => SystemNamespaceType.Create(aliasName, features),
SystemNamespaceType.BuiltInName => SystemNamespaceType.Create(aliasName, features, sourceFileKind),
{ } _ when builderDict.TryGetValue(providerName) is { } builderFunc => builderFunc(aliasName),
_ => null,
};
Expand Down
15 changes: 15 additions & 0 deletions src/Bicep.Core.IntegrationTests/ParameterFileTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -80,5 +80,20 @@ public void Params_file_with_not_using_declaration_should_log_diagnostic()
});
}
}

[TestMethod]
public void Parameters_file_cannot_reference_non_existing_env_variable()
{
var result = CompilationHelper.CompileParams(
("parameters.bicepparam", @"
using 'foo.bicep'
param fromEnv=readEnvironmentVariable('stringEnvVariable')
"),
("foo.bicep", @"param fromEnv string"));

result.ExcludingLinterDiagnostics().Should().HaveDiagnostics(new []{
("BCP338", DiagnosticLevel.Error,
"Failed to evaluate parameter \"stringEnvVariable\": Environment variable does not exist, and no default value set")});
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -80,5 +80,12 @@ string getLoggingString(ParameterAssignmentSymbol symbol)
data.Symbols.WriteToOutputFolder(sourceTextWithDiags);
data.Symbols.ShouldHaveExpectedValue();
}
[TestInitialize]
public void testInit()
{
System.Environment.SetEnvironmentVariable("stringEnvVariableName", "test");
System.Environment.SetEnvironmentVariable("intEnvVariableName", "100");
System.Environment.SetEnvironmentVariable("boolEnvironmentVariable", "true");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,14 @@ public async Task ProgramsShouldProduceExpectedIrTree(DataSet dataSet)
actualLocation: resultsFile);
}

[TestInitialize]
public void testInit()
{
System.Environment.SetEnvironmentVariable("stringEnvVariableName", "test");
System.Environment.SetEnvironmentVariable("intEnvVariableName", "100");
System.Environment.SetEnvironmentVariable("boolEnvironmentVariable", "true");
}

private static List<SyntaxBase> GetAllBoundSymbolReferences(ProgramSyntax program)
{
return SyntaxAggregator.Aggregate(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -216,3 +216,8 @@ param decoratedArray array = [
utcNow()
newGuid()
]

param stringfromEnvironmentVariables string
param intfromEnvironmentVariables int
param boolfromEnvironmentVariables bool
param intfromEnvironmentVariablesDefault int
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,7 @@ param additionalMetadata = 'more metadata'
param someParameter = 'three'
param stringLiteral = 'abc'
param decoratedString = 'Apple'
param stringfromEnvironmentVariables = readEnvironmentVariable('stringEnvVariableName')
param intfromEnvironmentVariables = int(readEnvironmentVariable('intEnvVariableName'))
param boolfromEnvironmentVariables = bool(readEnvironmentVariable('boolEnvironmentVariable'))
param intfromEnvironmentVariablesDefault = int(readEnvironmentVariable('intDefaultEnvVariableName','12'))
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,8 @@ param additionalMetadata = 'more metadata'
param someParameter = 'three'
param stringLiteral = 'abc'
param decoratedString = 'Apple'
param stringfromEnvironmentVariables = readEnvironmentVariable('stringEnvVariableName')
param intfromEnvironmentVariables = int(readEnvironmentVariable('intEnvVariableName'))
param boolfromEnvironmentVariables = bool(readEnvironmentVariable('boolEnvironmentVariable'))
param intfromEnvironmentVariablesDefault = int(readEnvironmentVariable('intDefaultEnvVariableName','12'))

Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,7 @@ param additionalMetadata = 'more metadata'
param someParameter = 'three'
param stringLiteral = 'abc'
param decoratedString = 'Apple'
param stringfromEnvironmentVariables = readEnvironmentVariable('stringEnvVariableName')
param intfromEnvironmentVariables = int(readEnvironmentVariable('intEnvVariableName'))
param boolfromEnvironmentVariables = bool(readEnvironmentVariable('boolEnvironmentVariable'))
param intfromEnvironmentVariablesDefault = int(readEnvironmentVariable('intDefaultEnvVariableName', '12'))
Original file line number Diff line number Diff line change
@@ -1,62 +1,74 @@
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"myString": {
"value": "hello world!!"
},
"myInt": {
"value": 42
},
"myBool": {
"value": false
},
"numberOfVMs": {
"value": 1
},
"password": {
"value": "strongPassword"
},
"secretObject": {
"value": {
"name": "vm2",
"location": "westus"
}
},
"storageSku": {
"value": "Standard_LRS"
},
"storageName": {
"value": "myStorage"
},
"someArray": {
"value": [
"a",
"b",
"c",
"d"
]
},
"emptyMetadata": {
"value": "empty!"
},
"description": {
"value": "descriptive description"
},
"description2": {
"value": "also descriptive"
},
"additionalMetadata": {
"value": "more metadata"
},
"someParameter": {
"value": "three"
},
"stringLiteral": {
"value": "abc"
},
"decoratedString": {
"value": "Apple"
}
}
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"myString": {
"value": "hello world!!"
},
"myInt": {
"value": 42
},
"myBool": {
"value": false
},
"numberOfVMs": {
"value": 1
},
"password": {
"value": "strongPassword"
},
"secretObject": {
"value": {
"name": "vm2",
"location": "westus"
}
},
"storageSku": {
"value": "Standard_LRS"
},
"storageName": {
"value": "myStorage"
},
"someArray": {
"value": [
"a",
"b",
"c",
"d"
]
},
"emptyMetadata": {
"value": "empty!"
},
"description": {
"value": "descriptive description"
},
"description2": {
"value": "also descriptive"
},
"additionalMetadata": {
"value": "more metadata"
},
"someParameter": {
"value": "three"
},
"stringLiteral": {
"value": "abc"
},
"decoratedString": {
"value": "Apple"
},
"stringfromEnvironmentVariables": {
"value": "test"
},
"intfromEnvironmentVariables": {
"value": 100
},
"boolfromEnvironmentVariables": {
"value": true
},
"intfromEnvironmentVariablesDefault": {
"value": 12
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,12 @@ param stringLiteral = 'abc'
//@[6:19) ParameterAssignment stringLiteral. Type: 'abc'. Declaration start char: 0, length: 27
param decoratedString = 'Apple'
//@[6:21) ParameterAssignment decoratedString. Type: 'Apple'. Declaration start char: 0, length: 31
param stringfromEnvironmentVariables = readEnvironmentVariable('stringEnvVariableName')
//@[6:36) ParameterAssignment stringfromEnvironmentVariables. Type: 'test'. Declaration start char: 0, length: 87
param intfromEnvironmentVariables = int(readEnvironmentVariable('intEnvVariableName'))
//@[6:33) ParameterAssignment intfromEnvironmentVariables. Type: int. Declaration start char: 0, length: 86
param boolfromEnvironmentVariables = bool(readEnvironmentVariable('boolEnvironmentVariable'))
//@[6:34) ParameterAssignment boolfromEnvironmentVariables. Type: bool. Declaration start char: 0, length: 93
param intfromEnvironmentVariablesDefault = int(readEnvironmentVariable('intDefaultEnvVariableName','12'))
//@[6:40) ParameterAssignment intfromEnvironmentVariablesDefault. Type: int. Declaration start char: 0, length: 105

Loading

0 comments on commit b7859f8

Please sign in to comment.