Merge pull request #5970 from boumenot/pr-azure-plan-info

azure: support for marketplace plan information
This commit is contained in:
Megan Marsh 2018-03-09 15:21:03 -08:00 committed by GitHub
commit b1eaaed6fb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 719 additions and 0 deletions

View File

@ -57,6 +57,13 @@ var (
reResourceGroupName = regexp.MustCompile(validResourceGroupNameRe) reResourceGroupName = regexp.MustCompile(validResourceGroupNameRe)
) )
type PlanInformation struct {
PlanName string `mapstructure:"plan_name"`
PlanProduct string `mapstructure:"plan_product"`
PlanPublisher string `mapstructure:"plan_publisher"`
PlanPromotionCode string `mapstructure:"plan_promotion_code"`
}
type Config struct { type Config struct {
common.PackerConfig `mapstructure:",squash"` common.PackerConfig `mapstructure:",squash"`
@ -107,6 +114,7 @@ type Config struct {
VirtualNetworkResourceGroupName string `mapstructure:"virtual_network_resource_group_name"` VirtualNetworkResourceGroupName string `mapstructure:"virtual_network_resource_group_name"`
CustomDataFile string `mapstructure:"custom_data_file"` CustomDataFile string `mapstructure:"custom_data_file"`
customData string customData string
PlanInfo PlanInformation `mapstructure:"plan_info"`
// OS // OS
OSType string `mapstructure:"os_type"` OSType string `mapstructure:"os_type"`
@ -647,6 +655,23 @@ func assertRequiredParametersSet(c *Config, errs *packer.MultiError) {
errs = packer.MultiErrorAppend(errs, fmt.Errorf("If virtual_network_subnet_name is specified, so must virtual_network_name")) errs = packer.MultiErrorAppend(errs, fmt.Errorf("If virtual_network_subnet_name is specified, so must virtual_network_name"))
} }
/////////////////////////////////////////////
// Plan Info
if c.PlanInfo.PlanName != "" || c.PlanInfo.PlanProduct != "" || c.PlanInfo.PlanPublisher != "" || c.PlanInfo.PlanPromotionCode != "" {
if c.PlanInfo.PlanName == "" || c.PlanInfo.PlanProduct == "" || c.PlanInfo.PlanPublisher == "" {
errs = packer.MultiErrorAppend(errs, fmt.Errorf("if either plan_name, plan_product, plan_publisher, or plan_promotion_code are defined then plan_name, plan_product, and plan_publisher must be defined"))
} else {
if c.AzureTags == nil {
c.AzureTags = make(map[string]*string)
}
c.AzureTags["PlanInfo"] = &c.PlanInfo.PlanName
c.AzureTags["PlanProduct"] = &c.PlanInfo.PlanProduct
c.AzureTags["PlanPublisher"] = &c.PlanInfo.PlanPublisher
c.AzureTags["PlanPromotionCode"] = &c.PlanInfo.PlanPromotionCode
}
}
///////////////////////////////////////////// /////////////////////////////////////////////
// OS // OS
if strings.EqualFold(c.OSType, constants.Target_Linux) { if strings.EqualFold(c.OSType, constants.Target_Linux) {

View File

@ -1097,6 +1097,137 @@ func TestConfigAdditionalDiskOverrideDefault(t *testing.T) {
} }
} }
// Test that configuration handles plan info
//
// The use of plan info requires that the following three properties are set.
//
// 1. plan_name
// 2. plan_product
// 3. plan_publisher
func TestPlanInfoConfiguration(t *testing.T) {
config := map[string]interface{}{
"capture_name_prefix": "ignore",
"capture_container_name": "ignore",
"image_offer": "ignore",
"image_publisher": "ignore",
"image_sku": "ignore",
"location": "ignore",
"storage_account": "ignore",
"resource_group_name": "ignore",
"subscription_id": "ignore",
"os_type": "linux",
"communicator": "none",
}
planInfo := map[string]string{
"plan_name": "--plan-name--",
}
config["plan_info"] = planInfo
_, _, err := newConfig(config, getPackerConfiguration())
if err == nil {
t.Fatal("expected config to reject the use of plan_name without plan_product and plan_publisher")
}
planInfo["plan_product"] = "--plan-product--"
_, _, err = newConfig(config, getPackerConfiguration())
if err == nil {
t.Fatal("expected config to reject the use of plan_name and plan_product without plan_publisher")
}
planInfo["plan_publisher"] = "--plan-publisher--"
c, _, err := newConfig(config, getPackerConfiguration())
if err != nil {
t.Fatalf("expected config to accept a complete plan configuration: %s", err)
}
if c.PlanInfo.PlanName != "--plan-name--" {
t.Fatalf("Expected PlanName to be '--plan-name--', but got %q", c.PlanInfo.PlanName)
}
if c.PlanInfo.PlanProduct != "--plan-product--" {
t.Fatalf("Expected PlanProduct to be '--plan-product--', but got %q", c.PlanInfo.PlanProduct)
}
if c.PlanInfo.PlanPublisher != "--plan-publisher--" {
t.Fatalf("Expected PlanPublisher to be '--plan-publisher--, but got %q", c.PlanInfo.PlanPublisher)
}
}
func TestPlanInfoPromotionCode(t *testing.T) {
config := map[string]interface{}{
"capture_name_prefix": "ignore",
"capture_container_name": "ignore",
"image_offer": "ignore",
"image_publisher": "ignore",
"image_sku": "ignore",
"location": "ignore",
"storage_account": "ignore",
"resource_group_name": "ignore",
"subscription_id": "ignore",
"os_type": "linux",
"communicator": "none",
"plan_info": map[string]string{
"plan_name": "--plan-name--",
"plan_product": "--plan-product--",
"plan_publisher": "--plan-publisher--",
"plan_promotion_code": "--plan-promotion-code--",
},
}
c, _, err := newConfig(config, getPackerConfiguration())
if err != nil {
t.Fatalf("expected config to accept plan_info configuration, but got %s", err)
}
if c.PlanInfo.PlanName != "--plan-name--" {
t.Fatalf("Expected PlanName to be '--plan-name--', but got %q", c.PlanInfo.PlanName)
}
if c.PlanInfo.PlanProduct != "--plan-product--" {
t.Fatalf("Expected PlanProduct to be '--plan-product--', but got %q", c.PlanInfo.PlanProduct)
}
if c.PlanInfo.PlanPublisher != "--plan-publisher--" {
t.Fatalf("Expected PlanPublisher to be '--plan-publisher--, but got %q", c.PlanInfo.PlanPublisher)
}
if c.PlanInfo.PlanPromotionCode != "--plan-promotion-code--" {
t.Fatalf("Expected PlanPublisher to be '--plan-promotion-code----, but got %q", c.PlanInfo.PlanPromotionCode)
}
}
// plan_info defines 3 or 4 tags based on plan data.
// The user can define up to 15 tags. If the combination of these two
// exceeds the max tag amount, the builder should reject the configuration.
func TestPlanInfoTooManyTagsErrors(t *testing.T) {
exactMaxNumberOfTags := map[string]string{}
for i := 0; i < 15; i++ {
exactMaxNumberOfTags[fmt.Sprintf("tag%.2d", i)] = "ignored"
}
config := map[string]interface{}{
"capture_name_prefix": "ignore",
"capture_container_name": "ignore",
"image_offer": "ignore",
"image_publisher": "ignore",
"image_sku": "ignore",
"location": "ignore",
"storage_account": "ignore",
"resource_group_name": "ignore",
"subscription_id": "ignore",
"os_type": "linux",
"communicator": "none",
"azure_tags": exactMaxNumberOfTags,
"plan_info": map[string]string{
"plan_name": "--plan-name--",
"plan_product": "--plan-product--",
"plan_publisher": "--plan-publisher--",
"plan_promotion_code": "--plan-promotion-code--",
},
}
_, _, err := newConfig(config, getPackerConfiguration())
if err == nil {
t.Fatal("expected config to reject configuration due to excess tags")
}
}
func getArmBuilderConfiguration() map[string]string { func getArmBuilderConfiguration() map[string]string {
m := make(map[string]string) m := make(map[string]string)
for _, v := range requiredConfigValues { for _, v := range requiredConfigValues {

View File

@ -81,6 +81,10 @@ func GetVirtualMachineDeployment(config *Config) (*resources.Deployment, error)
builder.SetCustomData(config.customData) builder.SetCustomData(config.customData)
} }
if config.PlanInfo.PlanName != "" {
builder.SetPlanInfo(config.PlanInfo.PlanName, config.PlanInfo.PlanProduct, config.PlanInfo.PlanPublisher, config.PlanInfo.PlanPromotionCode)
}
if config.VirtualNetworkName != "" && DefaultPrivateVirtualNetworkWithPublicIp != config.PrivateVirtualNetworkWithPublicIp { if config.VirtualNetworkName != "" && DefaultPrivateVirtualNetworkWithPublicIp != config.PrivateVirtualNetworkWithPublicIp {
builder.SetPrivateVirtualNetworWithPublicIp( builder.SetPrivateVirtualNetworWithPublicIp(
config.VirtualNetworkResourceGroupName, config.VirtualNetworkResourceGroupName,

View File

@ -0,0 +1,194 @@
{
"$schema": "http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json",
"contentVersion": "1.0.0.0",
"parameters": {
"adminPassword": {
"type": "string"
},
"adminUsername": {
"type": "string"
},
"dnsNameForPublicIP": {
"type": "string"
},
"osDiskName": {
"type": "string"
},
"storageAccountBlobEndpoint": {
"type": "string"
},
"vmName": {
"type": "string"
},
"vmSize": {
"type": "string"
}
},
"resources": [
{
"apiVersion": "[variables('publicIPAddressApiVersion')]",
"location": "[variables('location')]",
"name": "[variables('publicIPAddressName')]",
"properties": {
"dnsSettings": {
"domainNameLabel": "[parameters('dnsNameForPublicIP')]"
},
"publicIPAllocationMethod": "[variables('publicIPAddressType')]"
},
"tags": {
"PlanInfo": "planName00",
"PlanProduct": "planProduct00",
"PlanPromotionCode": "",
"PlanPublisher": "planPublisher00"
},
"type": "Microsoft.Network/publicIPAddresses"
},
{
"apiVersion": "[variables('virtualNetworksApiVersion')]",
"location": "[variables('location')]",
"name": "[variables('virtualNetworkName')]",
"properties": {
"addressSpace": {
"addressPrefixes": [
"[variables('addressPrefix')]"
]
},
"subnets": [
{
"name": "[variables('subnetName')]",
"properties": {
"addressPrefix": "[variables('subnetAddressPrefix')]"
}
}
]
},
"tags": {
"PlanInfo": "planName00",
"PlanProduct": "planProduct00",
"PlanPromotionCode": "",
"PlanPublisher": "planPublisher00"
},
"type": "Microsoft.Network/virtualNetworks"
},
{
"apiVersion": "[variables('networkInterfacesApiVersion')]",
"dependsOn": [
"[concat('Microsoft.Network/publicIPAddresses/', variables('publicIPAddressName'))]",
"[concat('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]"
],
"location": "[variables('location')]",
"name": "[variables('nicName')]",
"properties": {
"ipConfigurations": [
{
"name": "ipconfig",
"properties": {
"privateIPAllocationMethod": "Dynamic",
"publicIPAddress": {
"id": "[resourceId('Microsoft.Network/publicIPAddresses', variables('publicIPAddressName'))]"
},
"subnet": {
"id": "[variables('subnetRef')]"
}
}
}
]
},
"tags": {
"PlanInfo": "planName00",
"PlanProduct": "planProduct00",
"PlanPromotionCode": "",
"PlanPublisher": "planPublisher00"
},
"type": "Microsoft.Network/networkInterfaces"
},
{
"apiVersion": "[variables('apiVersion')]",
"dependsOn": [
"[concat('Microsoft.Network/networkInterfaces/', variables('nicName'))]"
],
"location": "[variables('location')]",
"name": "[parameters('vmName')]",
"plan": {
"name": "planName00",
"product": "planProduct00",
"publisher": "planPublisher00"
},
"properties": {
"diagnosticsProfile": {
"bootDiagnostics": {
"enabled": false
}
},
"hardwareProfile": {
"vmSize": "[parameters('vmSize')]"
},
"networkProfile": {
"networkInterfaces": [
{
"id": "[resourceId('Microsoft.Network/networkInterfaces', variables('nicName'))]"
}
]
},
"osProfile": {
"adminPassword": "[parameters('adminPassword')]",
"adminUsername": "[parameters('adminUsername')]",
"computerName": "[parameters('vmName')]",
"linuxConfiguration": {
"ssh": {
"publicKeys": [
{
"keyData": "",
"path": "[variables('sshKeyPath')]"
}
]
}
}
},
"storageProfile": {
"imageReference": {
"offer": "ignored00",
"publisher": "ignored00",
"sku": "ignored00",
"version": "latest"
},
"osDisk": {
"caching": "ReadWrite",
"createOption": "FromImage",
"name": "osdisk",
"vhd": {
"uri": "[concat(parameters('storageAccountBlobEndpoint'),variables('vmStorageAccountContainerName'),'/', parameters('osDiskName'),'.vhd')]"
}
}
}
},
"tags": {
"PlanInfo": "planName00",
"PlanProduct": "planProduct00",
"PlanPromotionCode": "",
"PlanPublisher": "planPublisher00"
},
"type": "Microsoft.Compute/virtualMachines"
}
],
"variables": {
"addressPrefix": "10.0.0.0/16",
"apiVersion": "2017-03-30",
"location": "[resourceGroup().location]",
"managedDiskApiVersion": "2017-03-30",
"networkInterfacesApiVersion": "2017-04-01",
"nicName": "packerNic",
"publicIPAddressApiVersion": "2017-04-01",
"publicIPAddressName": "packerPublicIP",
"publicIPAddressType": "Dynamic",
"sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]",
"subnetAddressPrefix": "10.0.0.0/24",
"subnetName": "packerSubnet",
"subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]",
"virtualNetworkName": "packerNetwork",
"virtualNetworkResourceGroup": "[resourceGroup().name]",
"virtualNetworksApiVersion": "2017-04-01",
"vmStorageAccountContainerName": "images",
"vnetID": "[resourceId(variables('virtualNetworkResourceGroup'), 'Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]"
}
}

View File

@ -0,0 +1,199 @@
{
"$schema": "http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json",
"contentVersion": "1.0.0.0",
"parameters": {
"adminPassword": {
"type": "string"
},
"adminUsername": {
"type": "string"
},
"dnsNameForPublicIP": {
"type": "string"
},
"osDiskName": {
"type": "string"
},
"storageAccountBlobEndpoint": {
"type": "string"
},
"vmName": {
"type": "string"
},
"vmSize": {
"type": "string"
}
},
"resources": [
{
"apiVersion": "[variables('publicIPAddressApiVersion')]",
"location": "[variables('location')]",
"name": "[variables('publicIPAddressName')]",
"properties": {
"dnsSettings": {
"domainNameLabel": "[parameters('dnsNameForPublicIP')]"
},
"publicIPAllocationMethod": "[variables('publicIPAddressType')]"
},
"tags": {
"PlanInfo": "planName00",
"PlanProduct": "planProduct00",
"PlanPromotionCode": "planPromotionCode00",
"PlanPublisher": "planPublisher00",
"dept": "engineering"
},
"type": "Microsoft.Network/publicIPAddresses"
},
{
"apiVersion": "[variables('virtualNetworksApiVersion')]",
"location": "[variables('location')]",
"name": "[variables('virtualNetworkName')]",
"properties": {
"addressSpace": {
"addressPrefixes": [
"[variables('addressPrefix')]"
]
},
"subnets": [
{
"name": "[variables('subnetName')]",
"properties": {
"addressPrefix": "[variables('subnetAddressPrefix')]"
}
}
]
},
"tags": {
"PlanInfo": "planName00",
"PlanProduct": "planProduct00",
"PlanPromotionCode": "planPromotionCode00",
"PlanPublisher": "planPublisher00",
"dept": "engineering"
},
"type": "Microsoft.Network/virtualNetworks"
},
{
"apiVersion": "[variables('networkInterfacesApiVersion')]",
"dependsOn": [
"[concat('Microsoft.Network/publicIPAddresses/', variables('publicIPAddressName'))]",
"[concat('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]"
],
"location": "[variables('location')]",
"name": "[variables('nicName')]",
"properties": {
"ipConfigurations": [
{
"name": "ipconfig",
"properties": {
"privateIPAllocationMethod": "Dynamic",
"publicIPAddress": {
"id": "[resourceId('Microsoft.Network/publicIPAddresses', variables('publicIPAddressName'))]"
},
"subnet": {
"id": "[variables('subnetRef')]"
}
}
}
]
},
"tags": {
"PlanInfo": "planName00",
"PlanProduct": "planProduct00",
"PlanPromotionCode": "planPromotionCode00",
"PlanPublisher": "planPublisher00",
"dept": "engineering"
},
"type": "Microsoft.Network/networkInterfaces"
},
{
"apiVersion": "[variables('apiVersion')]",
"dependsOn": [
"[concat('Microsoft.Network/networkInterfaces/', variables('nicName'))]"
],
"location": "[variables('location')]",
"name": "[parameters('vmName')]",
"plan": {
"name": "planName00",
"product": "planProduct00",
"promotionCode": "planPromotionCode00",
"publisher": "planPublisher00"
},
"properties": {
"diagnosticsProfile": {
"bootDiagnostics": {
"enabled": false
}
},
"hardwareProfile": {
"vmSize": "[parameters('vmSize')]"
},
"networkProfile": {
"networkInterfaces": [
{
"id": "[resourceId('Microsoft.Network/networkInterfaces', variables('nicName'))]"
}
]
},
"osProfile": {
"adminPassword": "[parameters('adminPassword')]",
"adminUsername": "[parameters('adminUsername')]",
"computerName": "[parameters('vmName')]",
"linuxConfiguration": {
"ssh": {
"publicKeys": [
{
"keyData": "",
"path": "[variables('sshKeyPath')]"
}
]
}
}
},
"storageProfile": {
"imageReference": {
"offer": "ignored00",
"publisher": "ignored00",
"sku": "ignored00",
"version": "latest"
},
"osDisk": {
"caching": "ReadWrite",
"createOption": "FromImage",
"name": "osdisk",
"vhd": {
"uri": "[concat(parameters('storageAccountBlobEndpoint'),variables('vmStorageAccountContainerName'),'/', parameters('osDiskName'),'.vhd')]"
}
}
}
},
"tags": {
"PlanInfo": "planName00",
"PlanProduct": "planProduct00",
"PlanPromotionCode": "planPromotionCode00",
"PlanPublisher": "planPublisher00",
"dept": "engineering"
},
"type": "Microsoft.Compute/virtualMachines"
}
],
"variables": {
"addressPrefix": "10.0.0.0/16",
"apiVersion": "2017-03-30",
"location": "[resourceGroup().location]",
"managedDiskApiVersion": "2017-03-30",
"networkInterfacesApiVersion": "2017-04-01",
"nicName": "packerNic",
"publicIPAddressApiVersion": "2017-04-01",
"publicIPAddressName": "packerPublicIP",
"publicIPAddressType": "Dynamic",
"sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]",
"subnetAddressPrefix": "10.0.0.0/24",
"subnetName": "packerSubnet",
"subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]",
"virtualNetworkName": "packerNetwork",
"virtualNetworkResourceGroup": "[resourceGroup().name]",
"virtualNetworksApiVersion": "2017-04-01",
"vmStorageAccountContainerName": "images",
"vnetID": "[resourceId(variables('virtualNetworkResourceGroup'), 'Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]"
}
}

View File

@ -523,3 +523,49 @@ func TestKeyVaultDeployment03(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
} }
func TestPlanInfo01(t *testing.T) {
planInfo := map[string]interface{}{
"plan_info": map[string]string{
"plan_name": "planName00",
"plan_product": "planProduct00",
"plan_publisher": "planPublisher00",
},
}
c, _, _ := newConfig(planInfo, getArmBuilderConfiguration(), getPackerConfiguration())
deployment, err := GetVirtualMachineDeployment(c)
if err != nil {
t.Fatal(err)
}
err = approvaltests.VerifyJSONStruct(t, deployment.Properties.Template)
if err != nil {
t.Fatal(err)
}
}
func TestPlanInfo02(t *testing.T) {
planInfo := map[string]interface{}{
"azure_tags": map[string]string{
"dept": "engineering",
},
"plan_info": map[string]string{
"plan_name": "planName00",
"plan_product": "planProduct00",
"plan_publisher": "planPublisher00",
"plan_promotion_code": "planPromotionCode00",
},
}
c, _, _ := newConfig(planInfo, getArmBuilderConfiguration(), getPackerConfiguration())
deployment, err := GetVirtualMachineDeployment(c)
if err != nil {
t.Fatal(err)
}
err = approvaltests.VerifyJSONStruct(t, deployment.Properties.Template)
if err != nil {
t.Fatal(err)
}
}

View File

@ -30,11 +30,19 @@ type Resource struct {
Type *string `json:"type"` Type *string `json:"type"`
Location *string `json:"location,omitempty"` Location *string `json:"location,omitempty"`
DependsOn *[]string `json:"dependsOn,omitempty"` DependsOn *[]string `json:"dependsOn,omitempty"`
Plan *Plan `json:"plan,omitempty"`
Properties *Properties `json:"properties,omitempty"` Properties *Properties `json:"properties,omitempty"`
Tags *map[string]*string `json:"tags,omitempty"` Tags *map[string]*string `json:"tags,omitempty"`
Resources *[]Resource `json:"resources,omitempty"` Resources *[]Resource `json:"resources,omitempty"`
} }
type Plan struct {
Name *string `json:"name"`
Product *string `json:"product"`
Publisher *string `json:"publisher"`
PromotionCode *string `json:"promotionCode,omitempty"`
}
type OSDiskUnion struct { type OSDiskUnion struct {
OsType compute.OperatingSystemTypes `json:"osType,omitempty"` OsType compute.OperatingSystemTypes `json:"osType,omitempty"`
OsState compute.OperatingSystemStateTypes `json:"osState,omitempty"` OsState compute.OperatingSystemStateTypes `json:"osState,omitempty"`

View File

@ -179,6 +179,26 @@ func (s *TemplateBuilder) SetImageUrl(imageUrl string, osType compute.OperatingS
return nil return nil
} }
func (s *TemplateBuilder) SetPlanInfo(name, product, publisher, promotionCode string) error {
var promotionCodeVal *string = nil
if promotionCode != "" {
promotionCodeVal = to.StringPtr(promotionCode)
}
for i, x := range *s.template.Resources {
if strings.EqualFold(*x.Type, resourceVirtualMachine) {
(*s.template.Resources)[i].Plan = &Plan{
Name: to.StringPtr(name),
Product: to.StringPtr(product),
Publisher: to.StringPtr(publisher),
PromotionCode: promotionCodeVal,
}
}
}
return nil
}
func (s *TemplateBuilder) SetOSDiskSizeGB(diskSizeGB int32) error { func (s *TemplateBuilder) SetOSDiskSizeGB(diskSizeGB int32) error {
resource, err := s.getResourceByType(resourceVirtualMachine) resource, err := s.getResourceByType(resourceVirtualMachine)
if err != nil { if err != nil {
@ -302,6 +322,17 @@ func (s *TemplateBuilder) getResourceByType(t string) (*Resource, error) {
return nil, fmt.Errorf("template: could not find a resource of type %s", t) return nil, fmt.Errorf("template: could not find a resource of type %s", t)
} }
func (s *TemplateBuilder) getResourceByType2(t string) (**Resource, error) {
for _, x := range *s.template.Resources {
if strings.EqualFold(*x.Type, t) {
p := &x
return &p, nil
}
}
return nil, fmt.Errorf("template: could not find a resource of type %s", t)
}
func (s *TemplateBuilder) setVariable(name string, value string) { func (s *TemplateBuilder) setVariable(name string, value string) {
(*s.template.Variables)[name] = value (*s.template.Variables)[name] = value
} }

View File

@ -0,0 +1,51 @@
{
"variables": {
"client_id": "{{env `ARM_CLIENT_ID`}}",
"client_secret": "{{env `ARM_CLIENT_SECRET`}}",
"resource_group": "{{env `ARM_RESOURCE_GROUP`}}",
"storage_account": "{{env `ARM_STORAGE_ACCOUNT`}}",
"subscription_id": "{{env `ARM_SUBSCRIPTION_ID`}}"
},
"builders": [{
"type": "azure-arm",
"client_id": "{{user `client_id`}}",
"client_secret": "{{user `client_secret`}}",
"resource_group_name": "{{user `resource_group`}}",
"storage_account": "{{user `storage_account`}}",
"subscription_id": "{{user `subscription_id`}}",
"capture_container_name": "images",
"capture_name_prefix": "packer",
"os_type": "Linux",
"image_publisher": "Canonical",
"image_offer": "UbuntuServer",
"image_sku": "16.04-LTS",
"azure_tags": {
"dept": "engineering",
"task": "image deployment"
},
"plan_info": {
"plan_name": "rabbitmq",
"plan_product": "rabbitmq",
"plan_publisher": "bitnami"
},
"location": "West US",
"vm_size": "Standard_DS2_v2"
}],
"provisioners": [{
"execute_command": "chmod +x {{ .Path }}; {{ .Vars }} sudo -E sh '{{ .Path }}'",
"inline": [
"apt-get update",
"apt-get upgrade -y",
"/usr/sbin/waagent -force -deprovision+user && export HISTSIZE=0 && sync"
],
"inline_shebang": "/bin/sh -x",
"type": "shell"
}]
}

View File

@ -162,6 +162,36 @@ Providing `temp_resource_group_name` or `location` in combination with `build_re
`Linux` this configures an SSH authorized key. For `Windows` this `Linux` this configures an SSH authorized key. For `Windows` this
configures a WinRM certificate. configures a WinRM certificate.
- `plan_info` (object) - Used for creating images from Marketplace images. Please refer to [Deploy an image with
Marketplace terms](https://aka.ms/azuremarketplaceapideployment) for more details. Not all Marketplace images
support programmatic deployment, and support is controlled by the image publisher.
An example plan_info object is defined below.
```json
{
"plan_info": {
"plan_name": "rabbitmq",
"plan_product": "rabbitmq",
"plan_publisher": "bitnami"
}
}
```
`plan_name` (string) - The plan name, required.
`plan_product` (string) - The plan product, required.
`plan_publisher` (string) - The plan publisher, required.
`plan_promotion_code` (string) - Some images accept a promotion code, optional.
Images created from the Marketplace with `plan_info` **must** specify `plan_info` whenever the image is deployed.
The builder automatically adds tags to the image to ensure this information is not lost. The following tags are
added.
1. PlanName
1. PlanProduct
1. PlanPublisher
1. PlanPromotionCode
- `temp_compute_name` (string) temporary name assigned to the VM. If this value is not set, a random value will be - `temp_compute_name` (string) temporary name assigned to the VM. If this value is not set, a random value will be
assigned. Knowing the resource group and VM name allows one to execute commands to update the VM during a Packer assigned. Knowing the resource group and VM name allows one to execute commands to update the VM during a Packer
build, e.g. attach a resource disk to the VM. build, e.g. attach a resource disk to the VM.