diff --git a/builder/azure/chroot/builder.go b/builder/azure/chroot/builder.go index 711146703..a5e26f9b3 100644 --- a/builder/azure/chroot/builder.go +++ b/builder/azure/chroot/builder.go @@ -115,6 +115,7 @@ type Builder struct { func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { b.config.ctx.Funcs = azcommon.TemplateFuncs + b.config.ctx.Funcs["vm"] = CreateVMMetadataTemplateFunc() err := config.Decode(&b.config, &config.DecodeOpts{ Interpolate: true, InterpolateContext: &b.config.ctx, @@ -129,6 +130,9 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { }, }, }, raws...) + if err != nil { + return nil, err + } var errs *packer.MultiError var warns []string diff --git a/builder/azure/chroot/template_funcs.go b/builder/azure/chroot/template_funcs.go new file mode 100644 index 000000000..7594cf949 --- /dev/null +++ b/builder/azure/chroot/template_funcs.go @@ -0,0 +1,37 @@ +package chroot + +import ( + "fmt" + "sync" + + "github.com/hashicorp/packer/builder/azure/common/client" +) + +// CreateVMMetadataTemplateFunc returns a template function that retrieves VM metadata. VM metadata is retrieved only once and reused for all executions of the function. +func CreateVMMetadataTemplateFunc() func(string) (string, error) { + var data *client.ComputeInfo + var dataErr error + once := sync.Once{} + return func(key string) (string, error) { + once.Do(func() { + data, dataErr = client.DefaultMetadataClient.GetComputeInfo() + }) + if dataErr != nil { + return "", dataErr + } + switch key { + case "name": + return data.Name, nil + case "subscription_id": + return data.SubscriptionID, nil + case "resource_group": + return data.ResourceGroupName, nil + case "location": + return data.Location, nil + case "resource_id": + return data.ResourceID(), nil + default: + return "", fmt.Errorf("unknown metadata key: %s (supported: name, subscription_id, resource_group, location, resource_id)", key) + } + } +} diff --git a/website/source/docs/builders/azure-chroot.html.md.erb b/website/source/docs/builders/azure-chroot.html.md.erb index 7244671a7..4a11152bd 100644 --- a/website/source/docs/builders/azure-chroot.html.md.erb +++ b/website/source/docs/builders/azure-chroot.html.md.erb @@ -101,7 +101,52 @@ mounts `/prod` and `/dev`: - The mount directory. -## Example +## Additional template function + +Because this builder runs on an Azure VM, there is an additional template function +available called `vm`, which returns the following VM metadata: + +- name +- subscription_id +- resource_group +- location +- resource_id + +This function can be used in the configuration templates, for example, use +``` +"{{ vm `subscription_id` }}" +``` +to fill in the subscription ID of the VM in any of the configuration options. + +## Examples +Here are some examples using this builder. + +### Using a VM with a Managed Identity +On a VM with a system-assigned managed identity that has the contributor role +on its own resource group, the following config can be used to create an +updated Debian image: +``` json +{ + "builders": [{ + "type": "azure-chroot", + + "command_wrapper": "sudo {{.Command}}", + + "image_resource_id": "/subscriptions/{{vm `subscription_id`}}/resourceGroups/{{vm `resource_group`}}/providers/Microsoft.Compute/images/MyDebianOSImage-{{timestamp}}", + "source": "credativ:Debian:9:latest" + }], + "provisioners": [{ + "inline": [ + "apt-get update", + "apt-get upgrade -y" + ], + "inline_shebang": "/bin/sh -x", + "type": "shell" + }] +} +``` + +### Using a Service Principal Here is an example that creates a Debian image with updated packages. Specify all environment variables (`ARM_CLIENT_ID`, `ARM_CLIENT_SECRET`, `ARM_SUBSCRIPTION_ID`) to use a service principal.