tooling/templatize/pkg/pipeline/bicep.go (102 lines of code) (raw):
// Copyright 2025 Microsoft Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package pipeline
import (
"context"
"encoding/json"
"fmt"
"os"
"os/exec"
"path/filepath"
"github.com/Azure/azure-sdk-for-go/sdk/azcore/to"
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources"
"github.com/Azure/ARO-Tools/pkg/config"
)
func transformBicepToARMWhatIfDeployment(ctx context.Context, bicepParameterTemplateFile string, cfg config.Configuration, inputs map[string]any) (*armresources.DeploymentWhatIfProperties, error) {
template, params, err := transformParameters(ctx, cfg, inputs, bicepParameterTemplateFile)
if err != nil {
return nil, err
}
return &armresources.DeploymentWhatIfProperties{
Mode: to.Ptr(armresources.DeploymentModeIncremental),
Template: template,
Parameters: params,
}, nil
}
func transformBicepToARMDeployment(ctx context.Context, bicepParameterTemplateFile string, cfg config.Configuration, inputs map[string]any) (*armresources.DeploymentProperties, error) {
template, params, err := transformParameters(ctx, cfg, inputs, bicepParameterTemplateFile)
if err != nil {
return nil, err
}
return &armresources.DeploymentProperties{
Mode: to.Ptr(armresources.DeploymentModeIncremental),
Template: template,
Parameters: params,
}, nil
}
func transformParameters(ctx context.Context, cfg config.Configuration, inputs map[string]any, bicepParameterTemplateFile string) (map[string]interface{}, map[string]interface{}, error) {
bicepParamContent, err := config.PreprocessFile(bicepParameterTemplateFile, cfg)
if err != nil {
return nil, nil, fmt.Errorf("failed to preprocess file: %w", err)
}
bicepParamBaseDir := filepath.Dir(bicepParameterTemplateFile)
bicepParamFile, err := os.CreateTemp(bicepParamBaseDir, "bicep-params-*.bicepparam")
if err != nil {
return nil, nil, fmt.Errorf("failed to create temp file: %w", err)
}
defer os.Remove(bicepParamFile.Name())
_, err = bicepParamFile.Write(bicepParamContent)
if err != nil {
return nil, nil, fmt.Errorf("failed to write to target file: %w", err)
}
cmd := exec.CommandContext(ctx, "az", "bicep", "build-params", "-f", bicepParamFile.Name(), "--stdout")
output, err := cmd.Output()
if err != nil {
combinedOutput, _ := cmd.CombinedOutput()
return nil, nil, fmt.Errorf("failed to get output from command: %w\n%s", err, string(combinedOutput))
}
var result generationResult
if err := json.Unmarshal(output, &result); err != nil {
return nil, nil, fmt.Errorf("failed to unmarshal output: %w", err)
}
template, err := result.Template()
if err != nil {
return nil, nil, fmt.Errorf("failed to get template: %w", err)
}
params, err := result.Parameters()
if err != nil {
return nil, nil, fmt.Errorf("failed to get parameters: %w", err)
}
for k, v := range inputs {
params[k] = map[string]interface{}{
"value": v,
}
}
return template, params, nil
}
type generationResult struct {
ParametersJson string `json:"parametersJson"`
TemplateJson string `json:"templateJson"`
}
func hasTemplateResources(template any) bool {
if templateAsMap, isMap := template.(map[string]interface{}); isMap {
if val, hasResources := templateAsMap["resources"]; hasResources {
if res, isList := val.([]any); isList {
return len(res) > 0
}
}
}
return false
}
func (gr generationResult) Parameters() (map[string]interface{}, error) {
var parameters = map[string]interface{}{}
if err := json.Unmarshal([]byte(gr.ParametersJson), ¶meters); err != nil {
return nil, fmt.Errorf("failed to unmarshal parameters: %w", err)
}
return parameters["parameters"].(map[string]interface{}), nil
}
func (gr generationResult) Template() (map[string]interface{}, error) {
var template map[string]interface{}
if err := json.Unmarshal([]byte(gr.TemplateJson), &template); err != nil {
return nil, fmt.Errorf("failed to unmarshal template: %w", err)
}
return template, nil
}