Skip to content

Commit ec24657

Browse files
shengloldingmeng-xueBethanyZhouisra-fel
authored
[Resources] Add Publish-AzBicepModule for publishing Bicep files (Azure#16159)
* Add Publish-AzBicepModule cmdlet * Update help * Add online version link * Minor refactoring * Update the order of changelog * Fix a typo Co-authored-by: Beisi Zhou <[email protected]> * Address comments * Apply suggestions from code review Path -> PathFile Co-authored-by: Yeming Liu <[email protected]> * Add license header * Update Publish-AzBicepModule.md * Update output type Co-authored-by: Dingmeng Xue <[email protected]> Co-authored-by: Beisi Zhou <[email protected]> Co-authored-by: Yeming Liu <[email protected]>
1 parent 4cd6b32 commit ec24657

File tree

6 files changed

+290
-81
lines changed

6 files changed

+290
-81
lines changed
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// ----------------------------------------------------------------------------------
2+
//
3+
// Copyright Microsoft Corporation
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
// Unless required by applicable law or agreed to in writing, software
9+
// distributed under the License is distributed on an "AS IS" BASIS,
10+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
// See the License for the specific language governing permissions and
12+
// limitations under the License.
13+
// ----------------------------------------------------------------------------------
14+
15+
using Microsoft.Azure.Commands.ResourceManager.Cmdlets.Utilities;
16+
using Microsoft.Azure.Commands.ResourceManager.Common;
17+
using Microsoft.WindowsAzure.Commands.Utilities.Common;
18+
using System.Management.Automation;
19+
20+
namespace Microsoft.Azure.Commands.ResourceManager.Cmdlets.Implementation.Bicep
21+
{
22+
[Cmdlet(VerbsData.Publish, AzureRMConstants.AzureRMPrefix + "BicepModule", SupportsShouldProcess = true), OutputType(typeof(bool))]
23+
public class PublishAzureBicepModuleCmdlet : AzureRMCmdlet
24+
{
25+
[Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true, HelpMessage = "Local path to the bicep file to publish.")]
26+
[ValidateNotNullOrEmpty]
27+
public string FilePath { get; set; }
28+
29+
[Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true, HelpMessage = "The target location where the bicep file will be published.")]
30+
[ValidateNotNullOrEmpty]
31+
public string Target { get; set; }
32+
33+
[Parameter(Mandatory = false, HelpMessage="Indicates that this cmdlet returns a boolean result. By default, this cmdlet does not generate any output.")]
34+
public SwitchParameter PassThru { get; set; }
35+
36+
public override void ExecuteCmdlet()
37+
{
38+
BicepUtility.PublishFile(this.TryResolvePath(this.FilePath), this.Target, this.WriteVerbose, this.WriteWarning);
39+
40+
if (this.PassThru.IsPresent)
41+
{
42+
this.WriteObject(true);
43+
}
44+
}
45+
}
46+
}

src/Resources/ResourceManager/Utilities/BicepUtility.cs

Lines changed: 100 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -12,132 +12,152 @@
1212
// limitations under the License.
1313
// ----------------------------------------------------------------------------------
1414

15-
using Microsoft.Azure.Commands.Common.Authentication.Abstractions;
16-
using Microsoft.Azure.Commands.Common.Exceptions;
17-
using Microsoft.WindowsAzure.Commands.Utilities.Common;
18-
19-
using System;
20-
using System.Collections.Generic;
21-
using System.IO;
22-
using System.Text.RegularExpressions;
23-
2415
namespace Microsoft.Azure.Commands.ResourceManager.Cmdlets.Utilities
2516
{
17+
using Microsoft.Azure.Commands.Common.Authentication.Abstractions;
18+
using Microsoft.Azure.Commands.Common.Exceptions;
19+
using Microsoft.WindowsAzure.Commands.Utilities.Common;
20+
using System;
21+
using System.IO;
22+
using System.Management.Automation;
23+
using System.Text.RegularExpressions;
24+
2625
internal static class BicepUtility
2726
{
28-
public static bool IsBicepExecutable { get; private set; } = false;
27+
private static bool IsBicepExecutable = false;
28+
29+
private const string MinimalVersionRequirement = "0.3.1";
30+
31+
private const string MinimalVersionRequirementForBicepPublish = "0.4.1008";
2932

30-
public static string MinimalVersionRequirement { get; private set; } = "0.3.1";
33+
public delegate void OutputCallback(string msg);
3134

32-
public static bool IsBicepFile(string templateFilePath)
35+
public static bool IsBicepFile(string templateFilePath) =>
36+
".bicep".Equals(Path.GetExtension(templateFilePath), StringComparison.OrdinalIgnoreCase);
37+
38+
public static string BuildFile(string bicepTemplateFilePath, OutputCallback writeVerbose = null, OutputCallback writeWarning = null)
3339
{
34-
return ".bicep".Equals(Path.GetExtension(templateFilePath), System.StringComparison.OrdinalIgnoreCase);
40+
if (!FileUtilities.DataStore.FileExists(bicepTemplateFilePath))
41+
{
42+
throw new AzPSArgumentException(Properties.Resources.InvalidBicepFilePath, "TemplateFile");
43+
}
44+
45+
string tempDirectory = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
46+
Directory.CreateDirectory(tempDirectory);
47+
48+
RunBicepCommand($"bicep build '{bicepTemplateFilePath}' --outdir '{tempDirectory}'", MinimalVersionRequirement, writeVerbose, writeWarning);
49+
50+
string buildResultPath = Path.Combine(tempDirectory, Path.GetFileName(bicepTemplateFilePath)).Replace(".bicep", ".json");
51+
if (!FileUtilities.DataStore.FileExists(buildResultPath))
52+
{
53+
throw new AzPSApplicationException(string.Format(Properties.Resources.BuildBicepFileToJsonFailed, bicepTemplateFilePath));
54+
}
55+
56+
return buildResultPath;
3557
}
3658

37-
public static bool CheckBicepExecutable()
59+
public static void PublishFile(string bicepFilePath, string target, OutputCallback writeVerbose = null, OutputCallback writeWarning = null)
3860
{
39-
System.Management.Automation.PowerShell powershell = System.Management.Automation.PowerShell.Create();
40-
powershell.AddScript("Get-Command bicep");
41-
powershell.Invoke();
42-
powershell.AddScript("$?");
43-
var result = powershell.Invoke();
44-
bool.TryParse(result[0].ToString(), out bool res);
45-
// Cache result
46-
IsBicepExecutable = res;
47-
return IsBicepExecutable;
61+
if (!FileUtilities.DataStore.FileExists(bicepFilePath))
62+
{
63+
throw new AzPSArgumentException(Properties.Resources.InvalidBicepFilePath, "File");
64+
}
65+
66+
RunBicepCommand($"bicep publish '{bicepFilePath}' --target '{target}'", MinimalVersionRequirementForBicepPublish, writeVerbose, writeWarning);
67+
}
68+
69+
private static void CheckBicepExecutable()
70+
{
71+
using (var powerShell = PowerShell.Create())
72+
{
73+
if (IsBicepExecutable)
74+
{
75+
return;
76+
}
77+
78+
powerShell.AddScript("Get-Command bicep");
79+
powerShell.Invoke();
80+
powerShell.AddScript("$?");
81+
var result = powerShell.Invoke();
82+
// Cache result
83+
bool.TryParse(result[0].ToString(), out IsBicepExecutable);
84+
85+
if (!IsBicepExecutable)
86+
{
87+
throw new AzPSApplicationException(Properties.Resources.BicepNotFound);
88+
}
89+
}
4890
}
4991

5092
private static string CheckMinimalVersionRequirement(string minimalVersionRequirement)
5193
{
52-
string currentBicepVersion = GetBicepVesion();
94+
string currentBicepVersion = GetBicepVersion();
5395
if (Version.Parse(minimalVersionRequirement).CompareTo(Version.Parse(currentBicepVersion)) > 0)
5496
{
5597
throw new AzPSApplicationException(string.Format(Properties.Resources.BicepVersionRequirement, minimalVersionRequirement));
5698
};
5799
return currentBicepVersion;
58100
}
59101

60-
public static string GetBicepVesion()
102+
private static string GetBicepVersion()
61103
{
62-
using(System.Management.Automation.PowerShell powershell = System.Management.Automation.PowerShell.Create())
104+
using (var powerShell = PowerShell.Create())
63105
{
64-
powershell.AddScript("bicep -v");
65-
var result = powershell.Invoke()[0].ToString();
106+
powerShell.AddScript("bicep -v");
107+
var result = powerShell.Invoke()[0].ToString();
66108
Regex pattern = new Regex("\\d+(\\.\\d+)+");
67109
string bicepVersion = pattern.Match(result)?.Value;
110+
68111
return bicepVersion;
69112
}
70113
}
71114

72-
public static int GetLastExitCode(System.Management.Automation.PowerShell powershell)
115+
private static int GetLastExitCode(PowerShell powershell)
73116
{
74117
powershell.AddScript("$LASTEXITCODE");
75118
var result = powershell.Invoke();
76119
int.TryParse(result[0]?.ToString(), out int exitcode);
77120
return exitcode;
78121
}
79122

80-
public delegate void VerboseOutputMethod(string msg);
81-
public delegate void WarningOutputMethod(string msg);
82-
83-
public static string BuildFile(string bicepTemplateFilePath, VerboseOutputMethod writeVerbose = null, WarningOutputMethod writeWarning = null)
123+
private static void RunBicepCommand(string command, string minimalVersionRequirement, OutputCallback writeVerbose = null, OutputCallback writeWarning = null)
84124
{
85-
if (!IsBicepExecutable && !CheckBicepExecutable())
86-
{
87-
throw new AzPSApplicationException(Properties.Resources.BicepNotFound);
88-
}
125+
CheckBicepExecutable();
89126

90-
string currentBicepVersion = CheckMinimalVersionRequirement(MinimalVersionRequirement);
127+
string currentBicepVersion = CheckMinimalVersionRequirement(minimalVersionRequirement);
91128

92-
string tempDirectory = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
93-
Directory.CreateDirectory(tempDirectory);
94-
95-
if (FileUtilities.DataStore.FileExists(bicepTemplateFilePath))
129+
using (var powerShell = PowerShell.Create())
96130
{
97-
using (System.Management.Automation.PowerShell powershell = System.Management.Automation.PowerShell.Create())
131+
powerShell.AddScript(command);
132+
var result = powerShell.Invoke();
133+
134+
if (writeVerbose != null)
98135
{
99-
powershell.AddScript($"bicep build '{bicepTemplateFilePath}' --outdir '{tempDirectory}'");
100-
var result = powershell.Invoke();
101-
102-
if (writeVerbose != null)
103-
{
104-
writeVerbose(string.Format("Using Bicep v{0}", currentBicepVersion));
105-
result.ForEach(r => writeVerbose(r.ToString()));
106-
}
136+
writeVerbose(string.Format("Using Bicep v{0}", currentBicepVersion));
137+
result.ForEach(r => writeVerbose(r.ToString()));
138+
}
107139

108-
// Bicep uses error stream to report warning message and error message, record it
109-
string warningOrErrorMsg = string.Empty;
110-
if (powershell.HadErrors)
111-
{
112-
powershell.Streams.Error.ForEach(e => { warningOrErrorMsg += (e + Environment.NewLine); });
113-
warningOrErrorMsg = warningOrErrorMsg.Substring(0, warningOrErrorMsg.Length - Environment.NewLine.Length);
114-
}
140+
// Bicep uses error stream to report warning message and error message, record it
141+
string warningOrErrorMsg = string.Empty;
142+
if (powerShell.HadErrors)
143+
{
144+
powerShell.Streams.Error.ForEach(e => { warningOrErrorMsg += (e + Environment.NewLine); });
145+
warningOrErrorMsg = warningOrErrorMsg.Substring(0, warningOrErrorMsg.Length - Environment.NewLine.Length);
146+
}
115147

116-
if (0 == GetLastExitCode(powershell))
117-
{
118-
// print warning message
119-
if(writeWarning != null && !string.IsNullOrEmpty(warningOrErrorMsg))
120-
{
121-
writeWarning(warningOrErrorMsg);
122-
}
123-
}
124-
else
148+
if (0 == GetLastExitCode(powerShell))
149+
{
150+
// print warning message
151+
if (writeWarning != null && !string.IsNullOrEmpty(warningOrErrorMsg))
125152
{
126-
throw new AzPSApplicationException(warningOrErrorMsg);
153+
writeWarning(warningOrErrorMsg);
127154
}
128155
}
156+
else
157+
{
158+
throw new AzPSApplicationException(warningOrErrorMsg);
159+
}
129160
}
130-
else
131-
{
132-
throw new AzPSArgumentException(Properties.Resources.InvalidBicepFilePath, "TemplateFile");
133-
}
134-
135-
string buildResultPath = Path.Combine(tempDirectory, Path.GetFileName(bicepTemplateFilePath)).Replace(".bicep", ".json");
136-
if (!FileUtilities.DataStore.FileExists(buildResultPath))
137-
{
138-
throw new AzPSApplicationException(string.Format(Properties.Resources.BuildBicepFileToJsonFailed, bicepTemplateFilePath));
139-
}
140-
return buildResultPath;
141161
}
142162
}
143-
}
163+
}

src/Resources/Resources/Az.Resources.psd1

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,8 @@ CmdletsToExport = 'Get-AzProviderOperation', 'Remove-AzRoleAssignment',
152152
'Get-AzManagementGroupDeploymentWhatIfResult',
153153
'Get-AzTenantDeploymentWhatIfResult', 'Get-AzTemplateSpec',
154154
'New-AzTemplateSpec', 'Set-AzTemplateSpec', 'Export-AzTemplateSpec',
155-
'Remove-AzTemplateSpec'
155+
'Remove-AzTemplateSpec',
156+
'Publish-AzBicepModule'
156157

157158
# Variables to export from this module
158159
# VariablesToExport = @()

src/Resources/Resources/ChangeLog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
-->
2020

2121
## Upcoming Release
22+
* Added new cmdlet `Publish-AzBicepModule` for publishing Bicep modules
2223

2324
## Version 4.4.1
2425
* Fixed a bug about the exitcode of Bicep [#16055]

src/Resources/Resources/help/Az.Resources.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,9 @@ Creates a new Template Spec.
234234
### [New-AzTenantDeployment](New-AzTenantDeployment.md)
235235
Create a deployment at tenant scope
236236

237+
### [Publish-AzBicepModule](Publish-AzBicepModule.md)
238+
Publishes a Bicep file to a registry.
239+
237240
### [Register-AzProviderFeature](Register-AzProviderFeature.md)
238241
Registers an Azure provider feature in your account.
239242

0 commit comments

Comments
 (0)