Skip to content

Commit a6e4e30

Browse files
committed
address PR review: extract bicepparam compile to helper, temp-file cleanup, docs fixes, exact assertion count
1 parent ab23e43 commit a6e4e30

5 files changed

Lines changed: 157 additions & 27 deletions

File tree

cmd/bicepCmd.go

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import (
3333
"github.com/Azure/mpf/pkg/domain"
3434
"github.com/Azure/mpf/pkg/infrastructure/ARMTemplateShared"
3535
"github.com/Azure/mpf/pkg/infrastructure/authorizationCheckers/ARMTemplateDeployment"
36+
"github.com/Azure/mpf/pkg/infrastructure/bicepUtils"
3637
"github.com/Azure/mpf/pkg/infrastructure/mpfSharedUtils"
3738
resourceGroupManager "github.com/Azure/mpf/pkg/infrastructure/resourceGroupManager"
3839
sproleassignmentmanager "github.com/Azure/mpf/pkg/infrastructure/spRoleAssignmentManager"
@@ -129,18 +130,25 @@ func getMPFBicep(cmd *cobra.Command, args []string) {
129130
log.Errorf("Error getting absolute path for parameters file: %v\n", err)
130131
}
131132

132-
// If the parameters file is a .bicepparam file, compile it to ARM JSON format
133-
if strings.HasSuffix(strings.ToLower(flgParametersFilePath), ".bicepparam") {
133+
// If the parameters file is a .bicepparam file, compile it to ARM JSON format.
134+
// Compilation goes to a temp file (rather than next to the source) so we never
135+
// silently clobber an existing "<name>.parameters.json" the user already has,
136+
// and so the artifact is reliably cleaned up on exit.
137+
if bicepUtils.IsBicepParamFile(flgParametersFilePath) {
134138
log.Infoln("Detected .bicepparam file, compiling to ARM parameters JSON format")
135-
compiledParamsPath := strings.TrimSuffix(flgParametersFilePath, filepath.Ext(flgParametersFilePath)) + ".parameters.json"
136-
buildParamsCmd := exec.Command(flgBicepExecPath, "build-params", flgParametersFilePath, "--outfile", compiledParamsPath)
137-
buildParamsCmd.Dir = filepath.Dir(flgParametersFilePath)
138139

139-
output, err := buildParamsCmd.CombinedOutput()
140+
compiledParamsPath, err := bicepUtils.CompileBicepParamsToTempFile(flgBicepExecPath, flgParametersFilePath)
140141
if err != nil {
141-
log.Fatalf("error running bicep build-params: %s\n%s", err, string(output))
142+
log.Fatalf("error compiling .bicepparam file: %v", err)
142143
}
143-
log.Infoln("Bicep parameters compiled successfully, ARM Parameters JSON created at:", compiledParamsPath)
144+
145+
defer func(path string) {
146+
if err := os.Remove(path); err != nil && !os.IsNotExist(err) {
147+
log.Warnf("error removing temporary ARM parameters file %q: %v", path, err)
148+
}
149+
}(compiledParamsPath)
150+
151+
log.Infoln("Bicep parameters compiled successfully, temporary ARM Parameters JSON created at:", compiledParamsPath)
144152
flgParametersFilePath = compiledParamsPath
145153
}
146154

docs/installation-and-quickstart.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,9 @@ $env:MPF_SPOBJECTID = "YOUR_SP_OBJECT_ID"
230230
$env:MPF_BICEPEXECPATH = (Get-Command bicep).Source # Dynamically resolves to the Bicep executable path, works across different installation locations
231231
232232
.\azmpf.exe bicep --bicepFilePath .\samples\bicep\storage-account-simple.bicep --parametersFilePath .\samples\bicep\storage-account-simple-params.json --jsonOutput --verbose
233-
```#### Bicep with .bicepparam Parameters File
233+
```
234+
235+
#### Bicep with .bicepparam Parameters File
234236

235237
MPF also supports Bicep-native `.bicepparam` parameter files. These are automatically compiled to ARM JSON format using `bicep build-params` before deployment. The `.bicepparam` file uses a `using` directive to reference the Bicep template:
236238

@@ -263,7 +265,7 @@ $env:MPF_SPCLIENTSECRET = "YOUR_SP_CLIENT_SECRET"
263265
$env:MPF_SPOBJECTID = "YOUR_SP_OBJECT_ID"
264266
$env:MPF_BICEPEXECPATH = (Get-Command bicep).Source
265267
266-
.\.azmpf.exe bicep --bicepFilePath .\samples\bicep\storage-account-simple.bicep --parametersFilePath .\samples\bicep\storage-account-simple-params.bicepparam --jsonOutput --verbose
268+
.\azmpf.exe bicep --bicepFilePath .\samples\bicep\storage-account-simple.bicep --parametersFilePath .\samples\bicep\storage-account-simple-params.bicepparam --jsonOutput --verbose
267269
```
268270

269271
### Terraform

e2eTests/e2eBicep_test.go

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import (
3232

3333
"github.com/Azure/mpf/pkg/infrastructure/ARMTemplateShared"
3434
"github.com/Azure/mpf/pkg/infrastructure/authorizationCheckers/ARMTemplateDeployment"
35+
"github.com/Azure/mpf/pkg/infrastructure/bicepUtils"
3536
mpfSharedUtils "github.com/Azure/mpf/pkg/infrastructure/mpfSharedUtils"
3637
rgm "github.com/Azure/mpf/pkg/infrastructure/resourceGroupManager"
3738
spram "github.com/Azure/mpf/pkg/infrastructure/spRoleAssignmentManager"
@@ -198,33 +199,37 @@ func TestBicepWithBicepparamFile(t *testing.T) {
198199
}
199200

200201
bicepExecPath := os.Getenv("MPF_BICEPEXECPATH")
201-
bicepFilePath := "../samples/bicep/storage-account-simple.bicep"
202-
parametersFilePath := "../samples/bicep/storage-account-simple-params.bicepparam"
203202

204-
bicepFilePath, _ = getAbsolutePath(bicepFilePath)
205-
parametersFilePath, _ = getAbsolutePath(parametersFilePath)
206-
207-
// Compile .bicepparam to ARM JSON parameters
208-
compiledParamsPath := strings.TrimSuffix(parametersFilePath, filepath.Ext(parametersFilePath)) + ".parameters.json"
209-
buildParamsCmd := exec.Command(bicepExecPath, "build-params", parametersFilePath, "--outfile", compiledParamsPath)
210-
buildParamsCmd.Dir = filepath.Dir(parametersFilePath)
203+
bicepFilePath, err := getAbsolutePath("../samples/bicep/storage-account-simple.bicep")
204+
if err != nil {
205+
t.Fatalf("failed to resolve absolute path for bicep file: %v", err)
206+
}
207+
parametersFilePath, err := getAbsolutePath("../samples/bicep/storage-account-simple-params.bicepparam")
208+
if err != nil {
209+
t.Fatalf("failed to resolve absolute path for parameters file: %v", err)
210+
}
211211

212-
output, err := buildParamsCmd.CombinedOutput()
212+
// Exercise the same compile helper used by the bicep CLI command so this
213+
// test covers the auto-compile-on-.bicepparam code path.
214+
if !bicepUtils.IsBicepParamFile(parametersFilePath) {
215+
t.Fatalf("expected %q to be detected as a .bicepparam file", parametersFilePath)
216+
}
217+
compiledParamsPath, err := bicepUtils.CompileBicepParamsToTempFile(bicepExecPath, parametersFilePath)
213218
if err != nil {
214-
log.Errorf("error running bicep build-params: %s\n%s", err, string(output))
215-
t.Fatal(err)
219+
t.Fatalf("error compiling .bicepparam file: %v", err)
216220
}
217221
t.Cleanup(func() { _ = os.Remove(compiledParamsPath) })
218222

219223
// Build bicep to ARM template
220224
armTemplatePath := strings.TrimSuffix(bicepFilePath, ".bicep") + ".json"
225+
t.Cleanup(func() { _ = os.Remove(armTemplatePath) })
226+
221227
bicepCmd := exec.Command(bicepExecPath, "build", bicepFilePath, "--outfile", armTemplatePath)
222228
bicepCmd.Dir = filepath.Dir(bicepFilePath)
223229

224-
_, err = bicepCmd.CombinedOutput()
230+
output, err := bicepCmd.CombinedOutput()
225231
if err != nil {
226-
log.Error(err)
227-
t.Error(err)
232+
t.Fatalf("error running bicep build: %s\n%s", err, string(output))
228233
}
229234

230235
ctx := t.Context()
@@ -256,13 +261,13 @@ func TestBicepWithBicepparamFile(t *testing.T) {
256261
t.Error(err)
257262
}
258263

259-
// Storage account deployment requires these permissions:
264+
// Storage account deployment requires exactly these 4 permissions:
260265
// Microsoft.Resources/deployments/read
261266
// Microsoft.Resources/deployments/write
262267
// Microsoft.Storage/storageAccounts/read
263268
// Microsoft.Storage/storageAccounts/write
264269
assert.NotEmpty(t, mpfResult.RequiredPermissions)
265-
assert.GreaterOrEqual(t, len(mpfResult.RequiredPermissions[mpfConfig.SubscriptionID]), 4)
270+
assert.Equal(t, 4, len(mpfResult.RequiredPermissions[mpfConfig.SubscriptionID]))
266271
}
267272

268273
func getAbsolutePath(path string) (string, error) {
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// MIT License
2+
//
3+
// Copyright (c) Microsoft Corporation.
4+
//
5+
// Permission is hereby granted, free of charge, to any person obtaining a copy
6+
// of this software and associated documentation files (the "Software"), to deal
7+
// in the Software without restriction, including without limitation the rights
8+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
// copies of the Software, and to permit persons to whom the Software is
10+
// furnished to do so, subject to the following conditions:
11+
//
12+
// The above copyright notice and this permission notice shall be included in all
13+
// copies or substantial portions of the Software.
14+
//
15+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
// SOFTWARE
22+
23+
// Package bicepUtils contains helpers for working with Bicep input files,
24+
// such as compiling .bicepparam files to ARM JSON parameter files.
25+
package bicepUtils
26+
27+
import (
28+
"fmt"
29+
"os"
30+
"os/exec"
31+
"path/filepath"
32+
"strings"
33+
)
34+
35+
// IsBicepParamFile reports whether the given path refers to a .bicepparam file.
36+
func IsBicepParamFile(path string) bool {
37+
return strings.HasSuffix(strings.ToLower(path), ".bicepparam")
38+
}
39+
40+
// CompileBicepParamsToTempFile compiles the given .bicepparam file to an ARM
41+
// JSON parameters file using `bicep build-params`. The output is written to a
42+
// freshly-created temp file, so any pre-existing "<name>.parameters.json" next
43+
// to the source file is left untouched.
44+
//
45+
// The caller is responsible for removing the returned path when no longer
46+
// needed (typically via defer / t.Cleanup). If an error occurs, the temp file
47+
// is removed before returning.
48+
func CompileBicepParamsToTempFile(bicepExecPath, paramsFilePath string) (string, error) {
49+
tempFile, err := os.CreateTemp("", "mpf-bicepparam-*.parameters.json")
50+
if err != nil {
51+
return "", fmt.Errorf("creating temporary ARM parameters file: %w", err)
52+
}
53+
compiledPath := tempFile.Name()
54+
if err := tempFile.Close(); err != nil {
55+
_ = os.Remove(compiledPath)
56+
return "", fmt.Errorf("closing temporary ARM parameters file: %w", err)
57+
}
58+
59+
cmd := exec.Command(bicepExecPath, "build-params", paramsFilePath, "--outfile", compiledPath)
60+
cmd.Dir = filepath.Dir(paramsFilePath)
61+
62+
output, err := cmd.CombinedOutput()
63+
if err != nil {
64+
_ = os.Remove(compiledPath)
65+
return "", fmt.Errorf("running bicep build-params: %w\n%s", err, string(output))
66+
}
67+
68+
return compiledPath, nil
69+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// MIT License
2+
//
3+
// Copyright (c) Microsoft Corporation.
4+
//
5+
// Permission is hereby granted, free of charge, to any person obtaining a copy
6+
// of this software and associated documentation files (the "Software"), to deal
7+
// in the Software without restriction, including without limitation the rights
8+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
// copies of the Software, and to permit persons to whom the Software is
10+
// furnished to do so, subject to the following conditions:
11+
//
12+
// The above copyright notice and this permission notice shall be included in all
13+
// copies or substantial portions of the Software.
14+
//
15+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
// SOFTWARE
22+
23+
package bicepUtils
24+
25+
import "testing"
26+
27+
func TestIsBicepParamFile(t *testing.T) {
28+
cases := []struct {
29+
path string
30+
want bool
31+
}{
32+
{"params.bicepparam", true},
33+
{"PARAMS.BICEPPARAM", true},
34+
{"/some/path/Params.BicepParam", true},
35+
{"params.json", false},
36+
{"main.bicep", false},
37+
{"", false},
38+
{"params.bicepparam.bak", false},
39+
}
40+
41+
for _, tc := range cases {
42+
if got := IsBicepParamFile(tc.path); got != tc.want {
43+
t.Errorf("IsBicepParamFile(%q) = %v, want %v", tc.path, got, tc.want)
44+
}
45+
}
46+
}

0 commit comments

Comments
 (0)