Skip to content

Commit 05b89d4

Browse files
Use utf8 encoding for reading stdout & stderr when invoking Bicep (#23409)
* Add BicepUtility test * Address PR feedback
1 parent f6bd606 commit 05b89d4

File tree

11 files changed

+204
-70
lines changed

11 files changed

+204
-70
lines changed

src/Resources/ResourceManager/Implementation/Bicep/PublishAzureBicepModuleCmdlet.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
// limitations under the License.
1313
// ----------------------------------------------------------------------------------
1414

15+
using Microsoft.Azure.Commands.Common.Authentication.Abstractions;
1516
using Microsoft.Azure.Commands.ResourceManager.Cmdlets.Utilities;
1617
using Microsoft.Azure.Commands.ResourceManager.Common;
1718
using Microsoft.WindowsAzure.Commands.Utilities.Common;
@@ -41,7 +42,7 @@ public class PublishAzureBicepModuleCmdlet : AzureRMCmdlet
4142

4243
public override void ExecuteCmdlet()
4344
{
44-
BicepUtility.PublishFile(this.TryResolvePath(this.FilePath), this.Target, this.DocumentationUri, this.Force.IsPresent, this.WriteVerbose, this.WriteWarning);
45+
BicepUtility.Create().PublishFile(this.TryResolvePath(this.FilePath), this.Target, this.DocumentationUri, this.Force.IsPresent, this.WriteVerbose, this.WriteWarning);
4546

4647
if (this.PassThru.IsPresent)
4748
{

src/Resources/ResourceManager/Implementation/CmdletBase/DeploymentCmdletBase.cs

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -475,7 +475,7 @@ protected string[] GetStaticParameterNames()
475475

476476
protected void BuildAndUseBicepTemplate()
477477
{
478-
TemplateFile = BicepUtility.BuildFile(this.ResolvePath(TemplateFile), this.WriteVerbose, this.WriteWarning);
478+
TemplateFile = BicepUtility.Create().BuildFile(this.ResolvePath(TemplateFile), this.WriteVerbose, this.WriteWarning);
479479
}
480480

481481
private IReadOnlyDictionary<string, object> GetDynamicParametersDictionary()
@@ -490,8 +490,8 @@ private IReadOnlyDictionary<string, object> GetDynamicParametersDictionary()
490490
protected void BuildAndUseBicepParameters(bool emitWarnings)
491491
{
492492
BicepUtility.OutputCallback nullCallback = null;
493-
var output = BicepUtility.BuildParams(this.ResolvePath(TemplateParameterFile), GetDynamicParametersDictionary(), this.WriteVerbose, emitWarnings ? this.WriteWarning : nullCallback);
494-
bicepparamFileParameters = GetParametersFromJson(output.parametersJson);
493+
var output = BicepUtility.Create().BuildParams(this.ResolvePath(TemplateParameterFile), GetDynamicParametersDictionary(), this.WriteVerbose, emitWarnings ? this.WriteWarning : nullCallback);
494+
bicepparamFileParameters = TemplateUtility.ParseTemplateParameterJson(output.parametersJson);
495495

496496
if (TemplateObject == null &&
497497
string.IsNullOrEmpty(TemplateFile) &&
@@ -545,13 +545,5 @@ private Hashtable GetCombinedTemplateParameterObject()
545545

546546
return TemplateParameterObject;
547547
}
548-
549-
private IReadOnlyDictionary<string, TemplateParameterFileParameter> GetParametersFromJson(string parametersJson)
550-
{
551-
using (var reader = new StringReader(parametersJson))
552-
{
553-
return TemplateUtility.ParseTemplateParameterJson(reader);
554-
}
555-
}
556548
}
557549
}

src/Resources/ResourceManager/Implementation/CmdletBase/DeploymentStacksCmdletBase.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ protected string ResolveBicepFile(string TemplateFile)
6060
{
6161
if (BicepUtility.IsBicepFile(TemplateFile))
6262
{
63-
return BicepUtility.BuildFile(this.ResolvePath(TemplateFile), this.WriteVerbose, this.WriteWarning);
63+
return BicepUtility.Create().BuildFile(this.ResolvePath(TemplateFile), this.WriteVerbose, this.WriteWarning);
6464
}
6565
else
6666
return TemplateFile;
@@ -71,7 +71,7 @@ protected BicepBuildParamsStdout ResolveBicepParameterFile(string TemplateParame
7171
{
7272
if (BicepUtility.IsBicepparamFile(TemplateParameterFile))
7373
{
74-
return BicepUtility.BuildParams(this.ResolvePath(TemplateParameterFile), new Dictionary<string, object>(), this.WriteVerbose, this.WriteWarning);
74+
return BicepUtility.Create().BuildParams(this.ResolvePath(TemplateParameterFile), new Dictionary<string, object>(), this.WriteVerbose, this.WriteWarning);
7575
}
7676

7777
return null;

src/Resources/ResourceManager/Implementation/TemplateSpecs/NewAzTemplateSpec.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ public override void ExecuteCmdlet()
166166

167167
if (BicepUtility.IsBicepFile(TemplateFile))
168168
{
169-
filePath = BicepUtility.BuildFile(this.ResolvePath(TemplateFile), this.WriteVerbose, this.WriteWarning);
169+
filePath = BicepUtility.Create().BuildFile(this.ResolvePath(TemplateFile), this.WriteVerbose, this.WriteWarning);
170170
}
171171

172172
// Note: We set uiFormDefinitionFilePath to null below because we process the UIFormDefinition

src/Resources/ResourceManager/Implementation/TemplateSpecs/SetAzTemplateSpec.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ public override void ExecuteCmdlet()
204204
}
205205
if (BicepUtility.IsBicepFile(TemplateFile))
206206
{
207-
filePath = BicepUtility.BuildFile(this.ResolvePath(TemplateFile), this.WriteVerbose, this.WriteWarning);
207+
filePath = BicepUtility.Create().BuildFile(this.ResolvePath(TemplateFile), this.WriteVerbose, this.WriteWarning);
208208
}
209209

210210
// Note: We set uiFormDefinitionFilePath to null below because we process the UIFormDefinition

src/Resources/ResourceManager/Utilities/BicepUtility.cs

Lines changed: 38 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ namespace Microsoft.Azure.Commands.ResourceManager.Cmdlets.Utilities
2323
using System.Collections.Generic;
2424
using System.IO;
2525
using System.Text.RegularExpressions;
26+
using Microsoft.Azure.Commands.Common.Authentication;
2627

2728
public class BicepBuildParamsStdout
2829
{
@@ -33,22 +34,10 @@ public class BicepBuildParamsStdout
3334
public string templateSpecId { get; set; }
3435
}
3536

36-
internal static class BicepUtility
37+
internal class BicepUtility
3738
{
38-
private static Lazy<string> BicepVersionLazy = new Lazy<string>(() => {
39-
var processInvoker = ProcessInvoker.Create();
40-
if (!processInvoker.CheckExecutableExists(BicepExecutable))
41-
{
42-
return null;
43-
}
44-
45-
var output = processInvoker.Invoke(new ProcessInput { Executable = BicepExecutable, Arguments = "-v" });
46-
47-
var pattern = new Regex("\\d+(\\.\\d+)+");
48-
return pattern.Match(output.Stdout)?.Value;
49-
});
50-
51-
private static string BicepVersion => BicepVersionLazy.Value;
39+
public static BicepUtility Create()
40+
=> new BicepUtility(ProcessInvoker.Create(), FileUtilities.DataStore);
5241

5342
/// <summary>
5443
/// The Bicep executable to use. By default, this'll be resolved from the system PATH.
@@ -72,15 +61,38 @@ internal static class BicepUtility
7261

7362
public delegate void OutputCallback(string msg);
7463

64+
private readonly IProcessInvoker processInvoker;
65+
private readonly IDataStore dataStore;
66+
private readonly Lazy<string> bicepVersionLazy;
67+
68+
public BicepUtility(IProcessInvoker processInvoker, IDataStore dataStore)
69+
{
70+
this.processInvoker = processInvoker;
71+
this.dataStore = dataStore;
72+
this.bicepVersionLazy = new Lazy<string>(() => {
73+
if (!processInvoker.CheckExecutableExists(BicepExecutable))
74+
{
75+
return null;
76+
}
77+
78+
var output = processInvoker.Invoke(new ProcessInput { Executable = BicepExecutable, Arguments = "-v" });
79+
80+
var pattern = new Regex("\\d+(\\.\\d+)+");
81+
return pattern.Match(output.Stdout)?.Value;
82+
});
83+
}
84+
85+
private string BicepVersion => bicepVersionLazy.Value;
86+
7587
public static bool IsBicepFile(string templateFilePath) =>
7688
".bicep".Equals(Path.GetExtension(templateFilePath), StringComparison.OrdinalIgnoreCase);
7789

7890
public static bool IsBicepparamFile(string parametersFilePath) =>
7991
".bicepparam".Equals(Path.GetExtension(parametersFilePath), StringComparison.OrdinalIgnoreCase);
8092

81-
public static string BuildFile(string bicepTemplateFilePath, OutputCallback writeVerbose = null, OutputCallback writeWarning = null)
93+
public string BuildFile(string bicepTemplateFilePath, OutputCallback writeVerbose = null, OutputCallback writeWarning = null)
8294
{
83-
if (!FileUtilities.DataStore.FileExists(bicepTemplateFilePath))
95+
if (!dataStore.FileExists(bicepTemplateFilePath))
8496
{
8597
throw new AzPSArgumentException(Properties.Resources.InvalidBicepFilePath, "TemplateFile");
8698
}
@@ -91,17 +103,17 @@ public static string BuildFile(string bicepTemplateFilePath, OutputCallback writ
91103
RunBicepCommand($"build {GetQuotedFilePath(bicepTemplateFilePath)} --outdir {GetQuotedFilePath(tempDirectory)}", MinimalVersionRequirement, writeVerbose, writeWarning);
92104

93105
string buildResultPath = Path.Combine(tempDirectory, Path.GetFileName(bicepTemplateFilePath)).Replace(".bicep", ".json");
94-
if (!FileUtilities.DataStore.FileExists(buildResultPath))
106+
if (!dataStore.FileExists(buildResultPath))
95107
{
96108
throw new AzPSApplicationException(string.Format(Properties.Resources.BuildBicepFileToJsonFailed, bicepTemplateFilePath));
97109
}
98110

99111
return buildResultPath;
100112
}
101113

102-
public static BicepBuildParamsStdout BuildParams(string bicepParamFilePath, IReadOnlyDictionary<string, object> overrideParams, OutputCallback writeVerbose = null, OutputCallback writeWarning = null)
114+
public BicepBuildParamsStdout BuildParams(string bicepParamFilePath, IReadOnlyDictionary<string, object> overrideParams, OutputCallback writeVerbose = null, OutputCallback writeWarning = null)
103115
{
104-
if (!FileUtilities.DataStore.FileExists(bicepParamFilePath))
116+
if (!dataStore.FileExists(bicepParamFilePath))
105117
{
106118
throw new AzPSArgumentException(Properties.Resources.InvalidBicepparamFilePath, "TemplateParameterFile");
107119
}
@@ -124,9 +136,9 @@ public static BicepBuildParamsStdout BuildParams(string bicepParamFilePath, IRea
124136
return JsonConvert.DeserializeObject<BicepBuildParamsStdout>(stdout);
125137
}
126138

127-
public static void PublishFile(string bicepFilePath, string target, string documentationUri = null, bool force = false, OutputCallback writeVerbose = null, OutputCallback writeWarning = null)
139+
public void PublishFile(string bicepFilePath, string target, string documentationUri = null, bool force = false, OutputCallback writeVerbose = null, OutputCallback writeWarning = null)
128140
{
129-
if (!FileUtilities.DataStore.FileExists(bicepFilePath))
141+
if (!dataStore.FileExists(bicepFilePath))
130142
{
131143
throw new AzPSArgumentException(Properties.Resources.InvalidBicepFilePath, "File");
132144
}
@@ -148,15 +160,15 @@ public static void PublishFile(string bicepFilePath, string target, string docum
148160
RunBicepCommand(bicepPublishCommand, MinimalVersionRequirementForBicepPublish, writeVerbose, writeWarning);
149161
}
150162

151-
private static void CheckBicepExecutable()
163+
private void CheckBicepExecutable()
152164
{
153165
if (BicepVersion == null)
154166
{
155167
throw new AzPSApplicationException(Properties.Resources.BicepNotFound);
156168
}
157169
}
158170

159-
private static string CheckMinimalVersionRequirement(string minimalVersionRequirement)
171+
private string CheckMinimalVersionRequirement(string minimalVersionRequirement)
160172
{
161173
CheckBicepExecutable();
162174

@@ -168,14 +180,13 @@ private static string CheckMinimalVersionRequirement(string minimalVersionRequir
168180
return BicepVersion;
169181
}
170182

171-
private static string RunBicepCommandWithStdoutCapture(string arguments, string minimalVersionRequirement, Dictionary<string, string> envVars = null, OutputCallback writeVerbose = null, OutputCallback writeWarning = null)
183+
private string RunBicepCommandWithStdoutCapture(string arguments, string minimalVersionRequirement, Dictionary<string, string> envVars = null, OutputCallback writeVerbose = null, OutputCallback writeWarning = null)
172184
{
173185
string currentBicepVersion = CheckMinimalVersionRequirement(minimalVersionRequirement);
174186
writeVerbose?.Invoke($"Using Bicep v{currentBicepVersion}");
175187

176188
writeVerbose?.Invoke($"Calling Bicep with arguments: {arguments}");
177189

178-
var processInvoker = ProcessInvoker.Create();
179190
var output = processInvoker.Invoke(new ProcessInput { Executable = BicepExecutable, Arguments = arguments, EnvVars = envVars });
180191

181192
if (output.ExitCode != 0)
@@ -192,14 +203,13 @@ private static string RunBicepCommandWithStdoutCapture(string arguments, string
192203
return output.Stdout;
193204
}
194205

195-
private static void RunBicepCommand(string arguments, string minimalVersionRequirement, OutputCallback writeVerbose = null, OutputCallback writeWarning = null)
206+
private void RunBicepCommand(string arguments, string minimalVersionRequirement, OutputCallback writeVerbose = null, OutputCallback writeWarning = null)
196207
{
197208
string currentBicepVersion = CheckMinimalVersionRequirement(minimalVersionRequirement);
198209
writeVerbose?.Invoke($"Using Bicep v{currentBicepVersion}");
199210

200211
writeVerbose?.Invoke($"Calling Bicep with arguments: {arguments}");
201212

202-
var processInvoker = ProcessInvoker.Create();
203213
var output = processInvoker.Invoke(new ProcessInput { Executable = BicepExecutable, Arguments = arguments });
204214

205215
writeVerbose?.Invoke(output.Stdout);

src/Resources/ResourceManager/Utilities/ProcessInvoker.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ namespace Microsoft.Azure.Commands.ResourceManager.Cmdlets.Utilities
1717
using System.Collections.Generic;
1818
using System.Diagnostics;
1919
using System.Management.Automation;
20+
using System.Text;
2021

2122
public class ProcessOutput
2223
{
@@ -84,6 +85,8 @@ public ProcessOutput Invoke(ProcessInput input)
8485
UseShellExecute = false,
8586
RedirectStandardOutput = true,
8687
RedirectStandardError = true,
88+
StandardOutputEncoding = Encoding.UTF8,
89+
StandardErrorEncoding = Encoding.UTF8,
8790
CreateNoWindow = true,
8891
}
8992
};

src/Resources/ResourceManager/Utilities/TemplateUtility.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,14 @@ public static Dictionary<string, TemplateParameterFileParameter> ParseTemplatePa
100100
}
101101
}
102102

103+
public static Dictionary<string, TemplateParameterFileParameter> ParseTemplateParameterJson(string json)
104+
{
105+
using (var reader = new StringReader(json))
106+
{
107+
return TemplateUtility.ParseTemplateParameterJson(reader);
108+
}
109+
}
110+
103111
public static Dictionary<string, TemplateParameterFileParameter> ParseTemplateParameterJson(TextReader reader)
104112
{
105113
// Read once to avoid having to rewind the stream

0 commit comments

Comments
 (0)