2019-05-31 08:27:41 -04:00
//go:generate struct-markdown
2019-10-14 10:43:59 -04:00
//go:generate mapstructure-to-hcl2 -type Config,SharedImageGallery,SharedImageGalleryDestination,PlanInformation
2019-05-31 08:27:41 -04:00
2016-03-04 05:14:55 -05:00
package arm
import (
2016-04-21 19:50:03 -04:00
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
2016-03-04 05:14:55 -05:00
"encoding/base64"
2016-04-21 19:50:03 -04:00
"encoding/json"
2016-03-04 05:14:55 -05:00
"fmt"
"io/ioutil"
2016-04-21 19:50:03 -04:00
"math/big"
2019-09-24 12:34:19 -04:00
"net"
2016-05-18 20:25:57 -04:00
"regexp"
"strings"
2016-03-04 05:14:55 -05:00
"time"
2020-03-24 09:43:24 -04:00
"github.com/hashicorp/packer/common/random"
2018-04-06 04:12:58 -04:00
"github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2018-04-01/compute"
2016-03-04 05:14:55 -05:00
"github.com/Azure/go-autorest/autorest/to"
2017-01-18 16:11:48 -05:00
"github.com/masterzen/winrm"
2016-05-18 20:25:57 -04:00
2019-05-31 18:37:43 -04:00
azcommon "github.com/hashicorp/packer/builder/azure/common"
"github.com/hashicorp/packer/builder/azure/common/client"
2017-04-04 16:39:01 -04:00
"github.com/hashicorp/packer/builder/azure/common/constants"
"github.com/hashicorp/packer/builder/azure/pkcs12"
"github.com/hashicorp/packer/common"
2020-03-13 13:04:48 -04:00
"github.com/hashicorp/packer/hcl2template"
2017-04-04 16:39:01 -04:00
"github.com/hashicorp/packer/helper/communicator"
"github.com/hashicorp/packer/helper/config"
"github.com/hashicorp/packer/packer"
"github.com/hashicorp/packer/template/interpolate"
2016-04-21 19:50:03 -04:00
"golang.org/x/crypto/ssh"
2016-03-04 05:14:55 -05:00
)
const (
2017-08-06 18:32:44 -04:00
DefaultImageVersion = "latest"
DefaultUserName = "packer"
DefaultPrivateVirtualNetworkWithPublicIp = false
DefaultVMSize = "Standard_A1"
2020-03-11 20:33:57 -04:00
DefaultKeyVaultSKU = "standard"
2016-03-04 05:14:55 -05:00
)
2017-11-17 12:49:23 -05:00
const (
// https://docs.microsoft.com/en-us/azure/architecture/best-practices/naming-conventions#naming-rules-and-restrictions
// Regular expressions in Go are not expressive enough, such that the regular expression returned by Azure
// can be used (no backtracking).
//
// -> ^[^_\W][\w-._]{0,79}(?<![-.])$
//
// This is not an exhaustive match, but it should be extremely close.
2018-07-11 11:31:51 -04:00
validResourceGroupNameRe = "^[^_\\W][\\w-._\\(\\)]{0,89}$"
2017-11-17 12:49:23 -05:00
validManagedDiskName = "^[^_\\W][\\w-._)]{0,79}$"
)
2016-05-18 20:25:57 -04:00
var (
2020-03-11 05:29:22 -04:00
reCaptureContainerName = regexp . MustCompile ( ` ^[a-z0-9][a-z0-9\-] { 2,62}$ ` )
reCaptureNamePrefix = regexp . MustCompile ( ` ^[A-Za-z0-9][A-Za-z0-9_\-\.] { 0,23}$ ` )
2017-11-17 12:49:23 -05:00
reManagedDiskName = regexp . MustCompile ( validManagedDiskName )
reResourceGroupName = regexp . MustCompile ( validResourceGroupNameRe )
2020-03-11 05:29:22 -04:00
reSnapshotName = regexp . MustCompile ( ` ^[A-Za-z0-9_] { 1,79}$ ` )
reSnapshotPrefix = regexp . MustCompile ( ` ^[A-Za-z0-9_] { 1,59}$ ` )
2016-05-18 20:25:57 -04:00
)
2018-03-09 01:39:23 -05:00
type PlanInformation struct {
PlanName string ` mapstructure:"plan_name" `
PlanProduct string ` mapstructure:"plan_product" `
PlanPublisher string ` mapstructure:"plan_publisher" `
PlanPromotionCode string ` mapstructure:"plan_promotion_code" `
}
2018-10-09 16:56:49 -04:00
type SharedImageGallery struct {
Subscription string ` mapstructure:"subscription" `
ResourceGroup string ` mapstructure:"resource_group" `
GalleryName string ` mapstructure:"gallery_name" `
ImageName string ` mapstructure:"image_name" `
2019-05-28 11:50:58 -04:00
// Specify a specific version of an OS to boot from.
2019-06-06 10:29:25 -04:00
// Defaults to latest. There may be a difference in versions available
// across regions due to image synchronization latency. To ensure a consistent
// version across regions set this value to one that is available in all
// regions where you are deploying.
ImageVersion string ` mapstructure:"image_version" required:"false" `
2018-10-09 16:56:49 -04:00
}
2019-06-03 20:47:29 -04:00
type SharedImageGalleryDestination struct {
SigDestinationResourceGroup string ` mapstructure:"resource_group" `
SigDestinationGalleryName string ` mapstructure:"gallery_name" `
SigDestinationImageName string ` mapstructure:"image_name" `
SigDestinationImageVersion string ` mapstructure:"image_version" `
SigDestinationReplicationRegions [ ] string ` mapstructure:"replication_regions" `
}
2016-03-04 05:14:55 -05:00
type Config struct {
common . PackerConfig ` mapstructure:",squash" `
// Authentication via OAUTH
2019-05-31 18:37:43 -04:00
ClientConfig client . Config ` mapstructure:",squash" `
2016-03-04 05:14:55 -05:00
// Capture
CaptureNamePrefix string ` mapstructure:"capture_name_prefix" `
CaptureContainerName string ` mapstructure:"capture_container_name" `
2019-06-06 10:45:44 -04:00
// Use a [Shared Gallery
// image](https://azure.microsoft.com/en-us/blog/announcing-the-public-preview-of-shared-image-gallery/)
// as the source for this build. *VHD targets are incompatible with this
// build type* - the target must be a *Managed Image*.
//
// "shared_image_gallery": {
// "subscription": "00000000-0000-0000-0000-00000000000",
// "resource_group": "ResourceGroup",
// "gallery_name": "GalleryName",
// "image_name": "ImageName",
// "image_version": "1.0.0"
// }
// "managed_image_name": "TargetImageName",
// "managed_image_resource_group_name": "TargetResourceGroup"
2019-05-28 11:50:58 -04:00
SharedGallery SharedImageGallery ` mapstructure:"shared_image_gallery" required:"false" `
2019-08-21 06:28:34 -04:00
// The name of the Shared Image Gallery under which the managed image will be published as Shared Gallery Image version.
//
// Following is an example.
//
// "shared_image_gallery_destination": {
// "resource_group": "ResourceGroup",
// "gallery_name": "GalleryName",
// "image_name": "ImageName",
// "image_version": "1.0.0",
// "replication_regions": ["regionA", "regionB", "regionC"]
// }
// "managed_image_name": "TargetImageName",
// "managed_image_resource_group_name": "TargetResourceGroup"
2019-06-03 20:47:29 -04:00
SharedGalleryDestination SharedImageGalleryDestination ` mapstructure:"shared_image_gallery_destination" `
2019-08-21 06:28:34 -04:00
// How long to wait for an image to be published to the shared image
// gallery before timing out. If your Packer build is failing on the
// Publishing to Shared Image Gallery step with the error `Original Error:
// context deadline exceeded`, but the image is present when you check your
// Azure dashboard, then you probably need to increase this timeout from
// its default of "60m" (valid time units include `s` for seconds, `m` for
// minutes, and `h` for hours.)
SharedGalleryTimeout time . Duration ` mapstructure:"shared_image_gallery_timeout" `
2020-01-02 02:23:04 -05:00
// The end of life date (2006-01-02T15:04:05.99Z) of the gallery Image Version. This property
// can be used for decommissioning purposes.
SharedGalleryImageVersionEndOfLifeDate string ` mapstructure:"shared_gallery_image_version_end_of_life_date" required:"false" `
// The number of replicas of the Image Version to be created per region. This
// property would take effect for a region when regionalReplicaCount is not specified.
// Replica count must be between 1 and 10.
SharedGalleryImageVersionReplicaCount int32 ` mapstructure:"shared_image_gallery_replica_count" required:"false" `
// If set to true, Virtual Machines deployed from the latest version of the
// Image Definition won't use this Image Version.
SharedGalleryImageVersionExcludeFromLatest bool ` mapstructure:"shared_gallery_image_version_exclude_from_latest" required:"false" `
2020-02-06 17:00:00 -05:00
// Name of the publisher to use for your base image (Azure Marketplace Images only). See
2019-06-06 10:45:44 -04:00
// [documentation](https://azure.microsoft.com/en-us/documentation/articles/resource-groups-vm-searching/)
2019-06-06 10:29:25 -04:00
// for details.
2019-06-06 10:45:44 -04:00
//
// CLI example `az vm image list-publishers --location westus`
2019-05-28 11:50:58 -04:00
ImagePublisher string ` mapstructure:"image_publisher" required:"true" `
2020-02-06 17:00:00 -05:00
// Name of the publisher's offer to use for your base image (Azure Marketplace Images only). See
2019-06-06 10:45:44 -04:00
// [documentation](https://azure.microsoft.com/en-us/documentation/articles/resource-groups-vm-searching/)
2019-06-06 10:29:25 -04:00
// for details.
2019-06-06 10:45:44 -04:00
//
// CLI example
// `az vm image list-offers --location westus --publisher Canonical`
2019-06-06 10:29:25 -04:00
ImageOffer string ` mapstructure:"image_offer" required:"true" `
2020-02-06 17:00:00 -05:00
// SKU of the image offer to use for your base image (Azure Marketplace Images only). See
2019-06-06 10:45:44 -04:00
// [documentation](https://azure.microsoft.com/en-us/documentation/articles/resource-groups-vm-searching/)
2019-06-06 10:29:25 -04:00
// for details.
2019-06-06 10:45:44 -04:00
//
// CLI example
// `az vm image list-skus --location westus --publisher Canonical --offer UbuntuServer`
2019-06-06 10:29:25 -04:00
ImageSku string ` mapstructure:"image_sku" required:"true" `
2019-05-28 11:50:58 -04:00
// Specify a specific version of an OS to boot from.
2019-06-06 10:45:44 -04:00
// Defaults to `latest`. There may be a difference in versions available
2019-06-06 10:29:25 -04:00
// across regions due to image synchronization latency. To ensure a consistent
// version across regions set this value to one that is available in all
// regions where you are deploying.
2019-06-06 10:45:44 -04:00
//
// CLI example
// `az vm image list --location westus --publisher Canonical --offer UbuntuServer --sku 16.04.0-LTS --all`
2019-06-06 10:29:25 -04:00
ImageVersion string ` mapstructure:"image_version" required:"false" `
2020-02-06 17:00:00 -05:00
// URL to a custom VHD to use for your base image. If this value is set, do
// not set image_publisher, image_offer, image_sku, or image_version.
ImageUrl string ` mapstructure:"image_url" required:"true" `
// Name of a custom managed image to use for your base image. If this value is set, do
2019-06-06 10:29:25 -04:00
// not set image_publisher, image_offer, image_sku, or image_version.
2019-06-06 10:45:44 -04:00
// If this value is set, the value
// `custom_managed_image_resource_group_name` must also be set. See
// [documentation](https://docs.microsoft.com/en-us/azure/storage/storage-managed-disks-overview#images)
2019-06-06 10:29:25 -04:00
// to learn more about managed images.
2020-02-06 17:00:00 -05:00
CustomManagedImageName string ` mapstructure:"custom_managed_image_name" required:"true" `
// Name of a custom managed image's resource group to use for your base image. If this
// value is set, image_publisher, image_offer, image_sku, or image_version.
// `custom_managed_image_name` must also be set. See
// [documentation](https://docs.microsoft.com/en-us/azure/storage/storage-managed-disks-overview#images)
// to learn more about managed images.
CustomManagedImageResourceGroupName string ` mapstructure:"custom_managed_image_resource_group_name" required:"true" `
customManagedImageID string
2017-05-29 00:06:09 -04:00
Location string ` mapstructure:"location" `
2019-06-06 10:45:44 -04:00
// Size of the VM used for building. This can be changed when you deploy a
// VM from your VHD. See
// [pricing](https://azure.microsoft.com/en-us/pricing/details/virtual-machines/)
// information. Defaults to `Standard_A1`.
//
// CLI example `az vm list-sizes --location westus`
2019-06-06 10:29:25 -04:00
VMSize string ` mapstructure:"vm_size" required:"false" `
2017-05-29 00:06:09 -04:00
2019-08-27 03:28:36 -04:00
// Specify the managed image resource group name where the result of the
// Packer build will be saved. The resource group must already exist. If
// this value is set, the value managed_image_name must also be set. See
// documentation to learn more about managed images.
2019-06-06 10:29:25 -04:00
ManagedImageResourceGroupName string ` mapstructure:"managed_image_resource_group_name" `
2019-08-27 03:28:36 -04:00
// Specify the managed image name where the result of the Packer build will
// be saved. The image name must not exist ahead of time, and will not be
// overwritten. If this value is set, the value
// managed_image_resource_group_name must also be set. See documentation to
// learn more about managed images.
ManagedImageName string ` mapstructure:"managed_image_name" `
2019-05-28 11:50:58 -04:00
// Specify the storage account
2019-06-06 10:29:25 -04:00
// type for a managed image. Valid values are Standard_LRS and Premium_LRS.
// The default is Standard_LRS.
ManagedImageStorageAccountType string ` mapstructure:"managed_image_storage_account_type" required:"false" `
managedImageStorageAccountType compute . StorageAccountTypes
2019-05-28 11:50:58 -04:00
// If
2019-06-06 10:29:25 -04:00
// managed_image_os_disk_snapshot_name is set, a snapshot of the OS disk
// is created with the same name as this value before the VM is captured.
ManagedImageOSDiskSnapshotName string ` mapstructure:"managed_image_os_disk_snapshot_name" required:"false" `
2019-05-28 11:50:58 -04:00
// If
2019-06-06 10:29:25 -04:00
// managed_image_data_disk_snapshot_prefix is set, snapshot of the data
// disk(s) is created with the same prefix as this value before the VM is
// captured.
2019-05-28 11:50:58 -04:00
ManagedImageDataDiskSnapshotPrefix string ` mapstructure:"managed_image_data_disk_snapshot_prefix" required:"false" `
2018-11-06 14:17:03 -05:00
manageImageLocation string
2019-06-06 10:45:44 -04:00
// Store the image in zone-resilient storage. You need to create it in a
// region that supports [availability
// zones](https://docs.microsoft.com/en-us/azure/availability-zones/az-overview).
2019-06-06 10:29:25 -04:00
ManagedImageZoneResilient bool ` mapstructure:"managed_image_zone_resilient" required:"false" `
2019-05-28 11:50:58 -04:00
// the user can define up to 15
2019-06-06 10:29:25 -04:00
// tags. Tag names cannot exceed 512 characters, and tag values cannot exceed
// 256 characters. Tags are applied to every resource deployed by a Packer
// build, i.e. Resource Group, VM, NIC, VNET, Public IP, KeyVault, etc.
2019-08-27 03:32:03 -04:00
AzureTags map [ string ] * string ` mapstructure:"azure_tags" required:"false" `
2020-03-16 07:19:34 -04:00
// Same as [`azure_tags`](#azure_tags) but defined as a singular repeatable block
2020-03-17 06:14:32 -04:00
// containing a `name` and a `value` field. In HCL2 mode the
2020-04-01 18:54:21 -04:00
// [`dynamic_block`](/docs/configuration/from-1.5/expressions#dynamic-blocks)
2020-03-13 13:04:48 -04:00
// will allow you to create those programatically.
2020-03-17 06:14:32 -04:00
AzureTag hcl2template . NameValues ` mapstructure:"azure_tag" required:"false" `
2019-08-27 03:32:03 -04:00
// Resource group under which the final artifact will be stored.
ResourceGroupName string ` mapstructure:"resource_group_name" `
// Storage account under which the final artifact will be stored.
StorageAccount string ` mapstructure:"storage_account" `
2019-05-28 11:50:58 -04:00
// temporary name assigned to the VM. If this
2019-06-06 10:29:25 -04:00
// 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 build, e.g. attach a resource disk to the VM.
2019-08-27 03:32:03 -04:00
TempComputeName string ` mapstructure:"temp_compute_name" required:"false" `
// name assigned to the temporary resource group created during the build.
// If this value is not set, a random value will be assigned. This resource
// group is deleted at the end of the build.
TempResourceGroupName string ` mapstructure:"temp_resource_group_name" `
// Specify an existing resource group to run the build in.
2020-01-23 17:28:54 -05:00
BuildResourceGroupName string ` mapstructure:"build_resource_group_name" `
// Specify an existing key vault to use for uploading certificates to the
// instance to connect.
2020-03-11 20:45:44 -04:00
BuildKeyVaultName string ` mapstructure:"build_key_vault_name" `
2020-03-11 21:43:37 -04:00
// Specify the KeyVault SKU to create during the build. Valid values are
// standard or premium. The default value is standard.
2020-03-11 20:45:44 -04:00
BuildKeyVaultSKU string ` mapstructure:"build_key_vault_sku" `
2019-06-06 10:29:25 -04:00
storageAccountBlobEndpoint string
2019-05-28 11:50:58 -04:00
// This value allows you to
2019-06-06 10:29:25 -04:00
// set a virtual_network_name and obtain a public IP. If this value is not
// set and virtual_network_name is defined Packer is only allowed to be
// executed from a host on the same subnet / virtual network.
PrivateVirtualNetworkWithPublicIp bool ` mapstructure:"private_virtual_network_with_public_ip" required:"false" `
2019-05-28 11:50:58 -04:00
// Use a pre-existing virtual network for the
2019-06-06 10:29:25 -04:00
// VM. This option enables private communication with the VM, no public IP
// address is used or provisioned (unless you set
// private_virtual_network_with_public_ip).
VirtualNetworkName string ` mapstructure:"virtual_network_name" required:"false" `
2019-05-28 11:50:58 -04:00
// If virtual_network_name is set,
2019-06-06 10:29:25 -04:00
// this value may also be set. If virtual_network_name is set, and this
// value is not set the builder attempts to determine the subnet to use with
// the virtual network. If the subnet cannot be found, or it cannot be
// disambiguated, this value should be set.
VirtualNetworkSubnetName string ` mapstructure:"virtual_network_subnet_name" required:"false" `
2019-05-28 11:50:58 -04:00
// If virtual_network_name is
2019-06-06 10:29:25 -04:00
// set, this value may also be set. If virtual_network_name is set, and
// this value is not set the builder attempts to determine the resource group
// containing the virtual network. If the resource group cannot be found, or
// it cannot be disambiguated, this value should be set.
VirtualNetworkResourceGroupName string ` mapstructure:"virtual_network_resource_group_name" required:"false" `
2019-06-06 10:45:44 -04:00
// Specify a file containing custom data to inject into the cloud-init
// process. The contents of the file are read and injected into the ARM
// template. The custom data will be passed to cloud-init for processing at
// the time of provisioning. See
// [documentation](http://cloudinit.readthedocs.io/en/latest/topics/examples.html)
2019-06-06 10:29:25 -04:00
// to learn more about custom data, and how it can be used to influence the
// provisioning process.
CustomDataFile string ` mapstructure:"custom_data_file" required:"false" `
customData string
2019-06-06 10:45:44 -04:00
// 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.
//
2020-03-12 10:05:08 -04:00
// ```json
2019-06-06 10:45:44 -04:00
// {
// "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
// 2. PlanProduct
// 3. PlanPublisher
// 4. PlanPromotionCode
//
2019-06-06 10:29:25 -04:00
PlanInfo PlanInformation ` mapstructure:"plan_info" required:"false" `
2019-10-11 18:29:02 -04:00
// The default PollingDuration for azure is 15mins, this property will override
// that value. See [Azure DefaultPollingDuration](https://godoc.org/github.com/Azure/go-autorest/autorest#pkg-constants)
// If your Packer build is failing on the
// ARM deployment step with the error `Original Error:
// context deadline exceeded`, then you probably need to increase this timeout from
// its default of "15m" (valid time units include `s` for seconds, `m` for
// minutes, and `h` for hours.)
PollingDurationTimeout time . Duration ` mapstructure:"polling_duration_timeout" required:"false" `
2019-05-28 11:50:58 -04:00
// If either Linux or Windows is specified Packer will
2019-06-06 10:29:25 -04:00
// automatically configure authentication credentials for the provisioned
// machine. For Linux this configures an SSH authorized key. For Windows
// this configures a WinRM certificate.
OSType string ` mapstructure:"os_type" required:"false" `
2019-05-28 11:50:58 -04:00
// Specify the size of the OS disk in GB
2019-06-06 10:29:25 -04:00
// (gigabytes). Values of zero or less than zero are ignored.
OSDiskSizeGB int32 ` mapstructure:"os_disk_size_gb" required:"false" `
2019-06-06 10:45:44 -04:00
// The size(s) of any additional hard disks for the VM in gigabytes. If
// this is not specified then the VM will only contain an OS disk. The
// number of additional disks and maximum size of a disk depends on the
// configuration of your VM. See
// [Windows](https://docs.microsoft.com/en-us/azure/virtual-machines/windows/about-disks-and-vhds)
2019-06-06 10:29:25 -04:00
// or
2019-06-06 10:45:44 -04:00
// [Linux](https://docs.microsoft.com/en-us/azure/virtual-machines/linux/about-disks-and-vhds)
2019-06-06 10:29:25 -04:00
// for more information.
2019-06-06 10:45:44 -04:00
//
// For VHD builds the final artifacts will be named
// `PREFIX-dataDisk-<n>.UUID.vhd` and stored in the specified capture
// container along side the OS disk. The additional disks are included in
// the deployment template `PREFIX-vmTemplate.UUID`.
//
// For Managed build the final artifacts are included in the managed image.
// The additional disk will have the same storage account type as the OS
// disk, as specified with the `managed_image_storage_account_type`
// setting.
2019-05-28 11:50:58 -04:00
AdditionalDiskSize [ ] int32 ` mapstructure:"disk_additional_size" required:"false" `
// Specify the disk caching type. Valid values
2019-06-06 10:29:25 -04:00
// are None, ReadOnly, and ReadWrite. The default value is ReadWrite.
DiskCachingType string ` mapstructure:"disk_caching_type" required:"false" `
diskCachingType compute . CachingTypes
2019-09-24 12:34:19 -04:00
// Specify the list of IP addresses and CIDR blocks that should be
// allowed access to the VM. If provided, an Azure Network Security
// Group will be created with corresponding rules and be bound to
2019-10-10 14:30:28 -04:00
// the subnet of the VM.
2019-10-08 17:56:43 -04:00
// Providing `allowed_inbound_ip_addresses` in combination with
// `virtual_network_name` is not allowed.
AllowedInboundIpAddresses [ ] string ` mapstructure:"allowed_inbound_ip_addresses" `
2018-02-23 18:34:13 -05:00
2016-03-04 05:14:55 -05:00
// Runtime Values
2020-03-03 11:13:39 -05:00
UserName string ` mapstructure-to-hcl2:",skip" `
Password string ` mapstructure-to-hcl2:",skip" `
2016-04-21 19:50:03 -04:00
tmpAdminPassword string
tmpCertificatePassword string
tmpResourceGroupName string
tmpComputeName string
2018-03-10 14:17:43 -05:00
tmpNicName string
tmpPublicIPAddressName string
2016-04-21 19:50:03 -04:00
tmpDeploymentName string
tmpKeyVaultName string
tmpOSDiskName string
2020-04-02 11:58:38 -04:00
tmpDataDiskName string
2018-03-10 14:17:43 -05:00
tmpSubnetName string
tmpVirtualNetworkName string
2019-10-08 17:56:43 -04:00
tmpNsgName string
2016-04-21 19:50:03 -04:00
tmpWinRMCertificateUrl string
2016-03-04 05:14:55 -05:00
// Authentication with the VM via SSH
sshAuthorizedKey string
2016-04-21 19:50:03 -04:00
// Authentication with the VM via WinRM
winrmCertificate string
2016-03-04 05:14:55 -05:00
Comm communicator . Config ` mapstructure:",squash" `
2018-08-01 02:35:29 -04:00
ctx interpolate . Context
2019-05-28 11:50:58 -04:00
// If you want packer to delete the
2019-06-06 10:29:25 -04:00
// temporary resource group asynchronously set this value. It's a boolean
// value and defaults to false. Important Setting this true means that
// your builds are faster, however any failed deletes are not reported.
2019-05-28 11:50:58 -04:00
AsyncResourceGroupDelete bool ` mapstructure:"async_resourcegroup_delete" required:"false" `
2016-03-04 05:14:55 -05:00
}
2016-04-21 19:50:03 -04:00
type keyVaultCertificate struct {
Data string ` json:"data" `
DataType string ` json:"dataType" `
Password string ` json:"password,omitempty" `
}
2017-05-29 00:06:09 -04:00
func ( c * Config ) toVMID ( ) string {
2017-11-09 06:20:09 -05:00
var resourceGroupName string
if c . tmpResourceGroupName != "" {
resourceGroupName = c . tmpResourceGroupName
} else {
resourceGroupName = c . BuildResourceGroupName
}
2019-05-31 18:37:43 -04:00
return fmt . Sprintf ( "/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Compute/virtualMachines/%s" , c . ClientConfig . SubscriptionID , resourceGroupName , c . tmpComputeName )
2017-05-29 00:06:09 -04:00
}
func ( c * Config ) isManagedImage ( ) bool {
2017-05-30 14:25:46 -04:00
return c . ManagedImageName != ""
2017-05-29 00:06:09 -04:00
}
2016-03-04 05:14:55 -05:00
func ( c * Config ) toVirtualMachineCaptureParameters ( ) * compute . VirtualMachineCaptureParameters {
return & compute . VirtualMachineCaptureParameters {
DestinationContainerName : & c . CaptureContainerName ,
VhdPrefix : & c . CaptureNamePrefix ,
OverwriteVhds : to . BoolPtr ( false ) ,
}
}
2017-05-29 00:06:09 -04:00
func ( c * Config ) toImageParameters ( ) * compute . Image {
return & compute . Image {
ImageProperties : & compute . ImageProperties {
SourceVirtualMachine : & compute . SubResource {
ID : to . StringPtr ( c . toVMID ( ) ) ,
} ,
2019-01-17 03:01:42 -05:00
StorageProfile : & compute . ImageStorageProfile {
ZoneResilient : to . BoolPtr ( c . ManagedImageZoneResilient ) ,
} ,
2017-05-29 00:06:09 -04:00
} ,
Location : to . StringPtr ( c . Location ) ,
2018-04-06 04:12:58 -04:00
Tags : c . AzureTags ,
2017-05-29 00:06:09 -04:00
}
}
2016-04-21 19:50:03 -04:00
func ( c * Config ) createCertificate ( ) ( string , error ) {
privateKey , err := rsa . GenerateKey ( rand . Reader , 2048 )
if err != nil {
2016-07-16 01:23:53 -04:00
err = fmt . Errorf ( "Failed to Generate Private Key: %s" , err )
2016-04-21 19:50:03 -04:00
return "" , err
}
host := fmt . Sprintf ( "%s.cloudapp.net" , c . tmpComputeName )
notBefore := time . Now ( )
2016-05-17 17:15:24 -04:00
notAfter := notBefore . Add ( 24 * time . Hour )
2016-04-21 19:50:03 -04:00
serialNumber , err := rand . Int ( rand . Reader , new ( big . Int ) . Lsh ( big . NewInt ( 1 ) , 128 ) )
if err != nil {
2016-07-16 01:23:53 -04:00
err = fmt . Errorf ( "Failed to Generate Serial Number: %v" , err )
2016-04-21 19:50:03 -04:00
return "" , err
}
template := x509 . Certificate {
SerialNumber : serialNumber ,
Issuer : pkix . Name {
CommonName : host ,
} ,
Subject : pkix . Name {
CommonName : host ,
} ,
NotBefore : notBefore ,
NotAfter : notAfter ,
KeyUsage : x509 . KeyUsageKeyEncipherment | x509 . KeyUsageDigitalSignature ,
ExtKeyUsage : [ ] x509 . ExtKeyUsage { x509 . ExtKeyUsageServerAuth } ,
BasicConstraintsValid : true ,
}
derBytes , err := x509 . CreateCertificate ( rand . Reader , & template , & template , & privateKey . PublicKey , privateKey )
if err != nil {
err = fmt . Errorf ( "Failed to Create Certificate: %s" , err )
return "" , err
}
pfxBytes , err := pkcs12 . Encode ( derBytes , privateKey , c . tmpCertificatePassword )
if err != nil {
err = fmt . Errorf ( "Failed to encode certificate as PFX: %s" , err )
return "" , err
}
keyVaultDescription := keyVaultCertificate {
Data : base64 . StdEncoding . EncodeToString ( pfxBytes ) ,
DataType : "pfx" ,
Password : c . tmpCertificatePassword ,
}
bytes , err := json . Marshal ( keyVaultDescription )
if err != nil {
err = fmt . Errorf ( "Failed to marshal key vault description: %s" , err )
return "" , err
}
return base64 . StdEncoding . EncodeToString ( bytes ) , nil
}
2019-12-17 05:25:56 -05:00
func ( c * Config ) Prepare ( raws ... interface { } ) ( [ ] string , error ) {
2019-05-31 18:37:43 -04:00
c . ctx . Funcs = azcommon . TemplateFuncs
2019-12-17 05:25:56 -05:00
err := config . Decode ( c , & config . DecodeOpts {
2016-03-04 05:14:55 -05:00
Interpolate : true ,
2018-08-01 02:35:29 -04:00
InterpolateContext : & c . ctx ,
2016-03-04 05:14:55 -05:00
} , raws ... )
if err != nil {
2019-12-17 05:25:56 -05:00
return nil , err
2016-03-04 05:14:55 -05:00
}
2019-12-17 05:25:56 -05:00
provideDefaultValues ( c )
setRuntimeValues ( c )
2020-03-24 09:43:24 -04:00
err = setUserNamePassword ( c )
if err != nil {
return nil , err
}
2020-03-16 10:12:13 -04:00
// copy singular blocks
for _ , kv := range c . AzureTag {
v := kv . Value
2020-03-17 06:14:32 -04:00
c . AzureTags [ kv . Name ] = & v
2020-03-16 10:12:13 -04:00
}
2019-05-31 18:37:43 -04:00
err = c . ClientConfig . SetDefaultValues ( )
2016-04-21 19:50:03 -04:00
if err != nil {
2019-12-17 05:25:56 -05:00
return nil , err
2016-04-21 19:50:03 -04:00
}
2016-03-04 05:14:55 -05:00
2019-12-17 05:25:56 -05:00
err = setCustomData ( c )
2016-10-13 14:56:23 -04:00
if err != nil {
2019-12-17 05:25:56 -05:00
return nil , err
2016-10-13 14:56:23 -04:00
}
2016-05-17 16:53:01 -04:00
// NOTE: if the user did not specify a communicator, then default to both
// SSH and WinRM. This is for backwards compatibility because the code did
2016-05-21 02:01:16 -04:00
// not specifically force the user to set a communicator.
2016-05-17 16:53:01 -04:00
if c . Comm . Type == "" || strings . EqualFold ( c . Comm . Type , "ssh" ) {
2019-12-17 05:25:56 -05:00
err = setSshValues ( c )
2016-05-17 16:53:01 -04:00
if err != nil {
2019-12-17 05:25:56 -05:00
return nil , err
2016-05-17 16:53:01 -04:00
}
2016-03-04 05:14:55 -05:00
}
2016-05-17 16:53:01 -04:00
if c . Comm . Type == "" || strings . EqualFold ( c . Comm . Type , "winrm" ) {
2019-12-17 05:25:56 -05:00
err = setWinRMCertificate ( c )
2016-05-17 16:53:01 -04:00
if err != nil {
2019-12-17 05:25:56 -05:00
return nil , err
2016-05-17 16:53:01 -04:00
}
2016-04-21 19:50:03 -04:00
}
2016-03-04 05:14:55 -05:00
var errs * packer . MultiError
2018-08-01 02:35:29 -04:00
errs = packer . MultiErrorAppend ( errs , c . Comm . Prepare ( & c . ctx ) ... )
2016-03-04 05:14:55 -05:00
2019-12-17 05:25:56 -05:00
assertRequiredParametersSet ( c , errs )
assertTagProperties ( c , errs )
2016-03-04 05:14:55 -05:00
if errs != nil && len ( errs . Errors ) > 0 {
2019-12-17 05:25:56 -05:00
return nil , errs
2016-03-04 05:14:55 -05:00
}
2019-12-17 05:25:56 -05:00
return nil , nil
2016-03-04 05:14:55 -05:00
}
func setSshValues ( c * Config ) error {
if c . Comm . SSHTimeout == 0 {
c . Comm . SSHTimeout = 20 * time . Minute
}
2018-08-23 10:35:07 -04:00
if c . Comm . SSHPrivateKeyFile != "" {
2018-11-06 14:03:33 -05:00
privateKeyBytes , err := c . Comm . ReadSSHPrivateKeyFile ( )
2016-03-04 05:14:55 -05:00
if err != nil {
2017-04-27 14:04:57 -04:00
return err
2016-03-04 05:14:55 -05:00
}
signer , err := ssh . ParsePrivateKey ( privateKeyBytes )
if err != nil {
2017-04-27 14:04:57 -04:00
return err
2016-03-04 05:14:55 -05:00
}
publicKey := signer . PublicKey ( )
c . sshAuthorizedKey = fmt . Sprintf ( "%s %s packer Azure Deployment%s" ,
publicKey . Type ( ) ,
base64 . StdEncoding . EncodeToString ( publicKey . Marshal ( ) ) ,
time . Now ( ) . Format ( time . RFC3339 ) )
2018-08-24 03:17:47 -04:00
c . Comm . SSHPrivateKey = privateKeyBytes
2016-03-04 05:14:55 -05:00
} else {
sshKeyPair , err := NewOpenSshKeyPair ( )
if err != nil {
return err
}
c . sshAuthorizedKey = sshKeyPair . AuthorizedKey ( )
2018-08-24 03:17:47 -04:00
c . Comm . SSHPrivateKey = sshKeyPair . PrivateKey ( )
2016-03-04 05:14:55 -05:00
}
return nil
}
2016-04-21 19:50:03 -04:00
func setWinRMCertificate ( c * Config ) error {
2017-01-18 16:11:48 -05:00
c . Comm . WinRMTransportDecorator =
2017-05-29 00:06:09 -04:00
func ( ) winrm . Transporter {
return & winrm . ClientNTLM { }
}
2016-05-17 16:53:01 -04:00
2016-04-21 19:50:03 -04:00
cert , err := c . createCertificate ( )
c . winrmCertificate = cert
return err
}
2016-03-04 05:14:55 -05:00
func setRuntimeValues ( c * Config ) {
var tempName = NewTempName ( )
c . tmpAdminPassword = tempName . AdminPassword
2018-09-10 19:48:42 -04:00
// store so that we can access this later during provisioning
2018-08-10 17:25:14 -04:00
packer . LogSecretFilter . Set ( c . tmpAdminPassword )
2018-09-10 19:48:42 -04:00
2016-04-21 19:50:03 -04:00
c . tmpCertificatePassword = tempName . CertificatePassword
2017-01-27 06:26:28 -05:00
if c . TempComputeName == "" {
c . tmpComputeName = tempName . ComputeName
} else {
c . tmpComputeName = c . TempComputeName
}
2016-03-04 05:14:55 -05:00
c . tmpDeploymentName = tempName . DeploymentName
2017-11-09 06:20:09 -05:00
// Only set tmpResourceGroupName if no name has been specified
if c . TempResourceGroupName == "" && c . BuildResourceGroupName == "" {
2017-01-27 06:26:28 -05:00
c . tmpResourceGroupName = tempName . ResourceGroupName
2017-11-09 06:20:09 -05:00
} else if c . TempResourceGroupName != "" && c . BuildResourceGroupName == "" {
2017-01-27 06:26:28 -05:00
c . tmpResourceGroupName = c . TempResourceGroupName
}
2018-03-10 14:17:43 -05:00
c . tmpNicName = tempName . NicName
c . tmpPublicIPAddressName = tempName . PublicIPAddressName
2016-03-04 05:14:55 -05:00
c . tmpOSDiskName = tempName . OSDiskName
2020-04-02 11:58:38 -04:00
c . tmpDataDiskName = tempName . DataDiskname
2018-03-10 14:17:43 -05:00
c . tmpSubnetName = tempName . SubnetName
c . tmpVirtualNetworkName = tempName . VirtualNetworkName
2019-10-08 17:56:43 -04:00
c . tmpNsgName = tempName . NsgName
2016-04-21 19:50:03 -04:00
c . tmpKeyVaultName = tempName . KeyVaultName
2016-03-04 05:14:55 -05:00
}
2020-03-24 09:43:24 -04:00
func setUserNamePassword ( c * Config ) error {
// SSH comm
2016-03-04 05:14:55 -05:00
if c . Comm . SSHUsername == "" {
c . Comm . SSHUsername = DefaultUserName
}
c . UserName = c . Comm . SSHUsername
if c . Comm . SSHPassword != "" {
c . Password = c . Comm . SSHPassword
2020-03-24 09:43:24 -04:00
return nil
}
// WinRM comm
if c . Comm . WinRMUser == "" {
c . Comm . WinRMUser = DefaultUserName
builder/azure-arm: Set WinRMPassword on the communicator config
Build results before change
```
azure-arm: output will be in this color.
==> azure-arm: Running builder ...
azure-arm:
==> azure-arm: Provisioning with Powershell...
==> azure-arm: Provisioning with powershell script:
/tmp/powershell-provisioner922851060
==> azure-arm: Exception calling "RegisterTaskDefinition" with "7" argument(s): "(38,4):Task:"
==> azure-arm: At C:\Windows\Temp\packer-elevated-shell-5e320d29-bdbd-b619-9e64-0c8a301b9d1d.p
==> azure-arm: s1:60 char:1 ==> azure-arm: + $f.RegisterTaskDefinition($name, $t, 6,
"packer", $password, $logon_type,
==> azure-arm: $null) ...
==> azure-arm: +
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
==> azure-arm: ~~~
==> azure-arm: + CategoryInfo : NotSpecified:
(:) [], MethodInvocationException
==> azure-arm: + FullyQualifiedErrorId :
ComMethodTargetInvocation
==> azure-arm:
==> azure-arm: Exception calling "GetTask" with "1"
argument(s): "The system cannot find the
==> azure-arm: file specified. (Exception from HRESULT:
0x80070002)"
==> azure-arm: At
C:\Windows\Temp\packer-elevated-shell-5e320d29-bdbd-b619-9e64-0c8a301b9d1d.p
==> azure-arm: s1:61 char:1
==> azure-arm: + $t = $f.GetTask("\$name")
==> azure-arm: + ~~~~~~~~~~~~~~~~~~~~~~~~~
==> azure-arm: + CategoryInfo : NotSpecified:
(:) [], MethodInvocationException
==> azure-arm: + FullyQualifiedErrorId :
ComMethodTargetInvocation
==> azure-arm:
==> azure-arm: Method invocation failed because
[System.__ComObject] does not contain a
==> azure-arm: method named 'Run'.
==> azure-arm: At
C:\Windows\Temp\packer-elevated-shell-5e320d29-bdbd-b619-9e64-0c8a301b9d1d.p
==> azure-arm: s1:62 char:1
==> azure-arm: + $t.Run($null) | Out-Null
==> azure-arm: + ~~~~~~~~~~~~~~~~~~~~~~~~
==> azure-arm: + CategoryInfo :
InvalidOperation: (Run:String) [], RuntimeExcept
==> azure-arm: ion
==> azure-arm: + FullyQualifiedErrorId : MethodNotFound
==> azure-arm:
Cancelling build after receiving interrupt
==> azure-arm: Removing the created Deployment object:
'pkrdp087bb80ibj'
==> azure-arm: Removing the created Deployment object:
'kvpkrdp087bb80ibj'
==> azure-arm:
==> azure-arm: Cleanup requested, deleting resource group
...
==> azure-arm: Resource group has been deleted.
Build 'azure-arm' errored: Build was cancelled.
Cleanly cancelled builds after being interrupted.
```
Build results after change
```
azure-arm: WinRM connected.
==> azure-arm: <Objs Version="1.1.0.1"
xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64
N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj><Obj S="progress" RefId="1"><TNRef RefId="0" /><MS><I64 N="SourceId">2</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj><Obj S="progress" RefId="2"><TNRef RefId="0" /><MS><I64 N="SourceId">3</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs>
==> azure-arm: Connected to WinRM!
==> azure-arm: Running local shell script:
/tmp/packer-shell091779215 azure-arm: 022xUtbwAH3DdqIoRCOh9caZi8tOYqcY
==> azure-arm: Provisioning with Powershell...
==> azure-arm: Provisioning with powershell script: /tmp/powershell-provisioner469853889
azure-arm: HELLO NEW USER; automatically generated aws password is: 022xUtbwAH3DdqIoRCOh9caZi8tOYqcY
==> azure-arm: Querying the machine's properties ...
```
2020-01-29 14:54:42 -05:00
}
2020-03-24 09:43:24 -04:00
c . UserName = c . Comm . WinRMUser
builder/azure-arm: Set WinRMPassword on the communicator config
Build results before change
```
azure-arm: output will be in this color.
==> azure-arm: Running builder ...
azure-arm:
==> azure-arm: Provisioning with Powershell...
==> azure-arm: Provisioning with powershell script:
/tmp/powershell-provisioner922851060
==> azure-arm: Exception calling "RegisterTaskDefinition" with "7" argument(s): "(38,4):Task:"
==> azure-arm: At C:\Windows\Temp\packer-elevated-shell-5e320d29-bdbd-b619-9e64-0c8a301b9d1d.p
==> azure-arm: s1:60 char:1 ==> azure-arm: + $f.RegisterTaskDefinition($name, $t, 6,
"packer", $password, $logon_type,
==> azure-arm: $null) ...
==> azure-arm: +
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
==> azure-arm: ~~~
==> azure-arm: + CategoryInfo : NotSpecified:
(:) [], MethodInvocationException
==> azure-arm: + FullyQualifiedErrorId :
ComMethodTargetInvocation
==> azure-arm:
==> azure-arm: Exception calling "GetTask" with "1"
argument(s): "The system cannot find the
==> azure-arm: file specified. (Exception from HRESULT:
0x80070002)"
==> azure-arm: At
C:\Windows\Temp\packer-elevated-shell-5e320d29-bdbd-b619-9e64-0c8a301b9d1d.p
==> azure-arm: s1:61 char:1
==> azure-arm: + $t = $f.GetTask("\$name")
==> azure-arm: + ~~~~~~~~~~~~~~~~~~~~~~~~~
==> azure-arm: + CategoryInfo : NotSpecified:
(:) [], MethodInvocationException
==> azure-arm: + FullyQualifiedErrorId :
ComMethodTargetInvocation
==> azure-arm:
==> azure-arm: Method invocation failed because
[System.__ComObject] does not contain a
==> azure-arm: method named 'Run'.
==> azure-arm: At
C:\Windows\Temp\packer-elevated-shell-5e320d29-bdbd-b619-9e64-0c8a301b9d1d.p
==> azure-arm: s1:62 char:1
==> azure-arm: + $t.Run($null) | Out-Null
==> azure-arm: + ~~~~~~~~~~~~~~~~~~~~~~~~
==> azure-arm: + CategoryInfo :
InvalidOperation: (Run:String) [], RuntimeExcept
==> azure-arm: ion
==> azure-arm: + FullyQualifiedErrorId : MethodNotFound
==> azure-arm:
Cancelling build after receiving interrupt
==> azure-arm: Removing the created Deployment object:
'pkrdp087bb80ibj'
==> azure-arm: Removing the created Deployment object:
'kvpkrdp087bb80ibj'
==> azure-arm:
==> azure-arm: Cleanup requested, deleting resource group
...
==> azure-arm: Resource group has been deleted.
Build 'azure-arm' errored: Build was cancelled.
Cleanly cancelled builds after being interrupted.
```
Build results after change
```
azure-arm: WinRM connected.
==> azure-arm: <Objs Version="1.1.0.1"
xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64
N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj><Obj S="progress" RefId="1"><TNRef RefId="0" /><MS><I64 N="SourceId">2</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj><Obj S="progress" RefId="2"><TNRef RefId="0" /><MS><I64 N="SourceId">3</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs>
==> azure-arm: Connected to WinRM!
==> azure-arm: Running local shell script:
/tmp/packer-shell091779215 azure-arm: 022xUtbwAH3DdqIoRCOh9caZi8tOYqcY
==> azure-arm: Provisioning with Powershell...
==> azure-arm: Provisioning with powershell script: /tmp/powershell-provisioner469853889
azure-arm: HELLO NEW USER; automatically generated aws password is: 022xUtbwAH3DdqIoRCOh9caZi8tOYqcY
==> azure-arm: Querying the machine's properties ...
```
2020-01-29 14:54:42 -05:00
if c . Comm . WinRMPassword == "" {
2020-03-24 09:43:24 -04:00
// Configure password settings using Azure generated credentials
c . Comm . WinRMPassword = c . tmpAdminPassword
2016-03-04 05:14:55 -05:00
}
2020-03-24 09:43:24 -04:00
if ! isValidPassword ( c . Comm . WinRMPassword ) {
return fmt . Errorf ( "The supplied \"winrm_password\" must be between 8-123 characters long and must satisfy at least 3 from the following: \n1) Contains an uppercase character \n2) Contains a lowercase character\n3) Contains a numeric digit\n4) Contains a special character\n5) Control characters are not allowed" )
}
c . Password = c . Comm . WinRMPassword
return nil
2016-03-04 05:14:55 -05:00
}
2016-10-13 14:56:23 -04:00
func setCustomData ( c * Config ) error {
if c . CustomDataFile == "" {
return nil
}
b , err := ioutil . ReadFile ( c . CustomDataFile )
if err != nil {
return err
}
c . customData = base64 . StdEncoding . EncodeToString ( b )
return nil
}
2016-03-04 05:14:55 -05:00
func provideDefaultValues ( c * Config ) {
if c . VMSize == "" {
c . VMSize = DefaultVMSize
}
2016-04-21 19:50:03 -04:00
2017-08-13 04:45:04 -04:00
if c . ManagedImageStorageAccountType == "" {
2018-04-06 04:12:58 -04:00
c . managedImageStorageAccountType = compute . StorageAccountTypesStandardLRS
2017-08-13 04:45:04 -04:00
}
2018-11-30 15:32:41 -05:00
if c . DiskCachingType == "" {
c . diskCachingType = compute . CachingTypesReadWrite
}
2017-05-29 00:06:09 -04:00
if c . ImagePublisher != "" && c . ImageVersion == "" {
2016-04-21 19:50:03 -04:00
c . ImageVersion = DefaultImageVersion
}
2020-03-11 20:33:57 -04:00
if c . BuildKeyVaultSKU == "" {
c . BuildKeyVaultSKU = DefaultKeyVaultSKU
}
2019-05-31 18:37:43 -04:00
c . ClientConfig . SetDefaultValues ( )
2016-03-04 05:14:55 -05:00
}
2016-07-29 17:17:33 -04:00
func assertTagProperties ( c * Config , errs * packer . MultiError ) {
if len ( c . AzureTags ) > 15 {
errs = packer . MultiErrorAppend ( errs , fmt . Errorf ( "a max of 15 tags are supported, but %d were provided" , len ( c . AzureTags ) ) )
}
for k , v := range c . AzureTags {
if len ( k ) > 512 {
errs = packer . MultiErrorAppend ( errs , fmt . Errorf ( "the tag name %q exceeds (%d) the 512 character limit" , k , len ( k ) ) )
}
if len ( * v ) > 256 {
2018-08-24 19:18:21 -04:00
errs = packer . MultiErrorAppend ( errs , fmt . Errorf ( "the tag name %q exceeds (%d) the 256 character limit" , * v , len ( * v ) ) )
2016-07-29 17:17:33 -04:00
}
}
}
2016-03-04 05:14:55 -05:00
func assertRequiredParametersSet ( c * Config , errs * packer . MultiError ) {
2019-05-31 18:37:43 -04:00
c . ClientConfig . Validate ( errs )
2016-03-04 05:14:55 -05:00
/////////////////////////////////////////////
// Capture
2017-06-07 18:01:10 -04:00
if c . CaptureContainerName == "" && c . ManagedImageName == "" {
errs = packer . MultiErrorAppend ( errs , fmt . Errorf ( "A capture_container_name or managed_image_name must be specified" ) )
2016-05-18 20:25:57 -04:00
}
2017-06-07 18:01:10 -04:00
if c . CaptureNamePrefix == "" && c . ManagedImageResourceGroupName == "" {
errs = packer . MultiErrorAppend ( errs , fmt . Errorf ( "A capture_name_prefix or managed_image_resource_group_name must be specified" ) )
2016-05-18 20:25:57 -04:00
}
2017-06-07 18:01:10 -04:00
if ( c . CaptureNamePrefix != "" || c . CaptureContainerName != "" ) && ( c . ManagedImageResourceGroupName != "" || c . ManagedImageName != "" ) {
errs = packer . MultiErrorAppend ( errs , fmt . Errorf ( "Either a VHD or a managed image can be built, but not both. Please specify either capture_container_name and capture_name_prefix or managed_image_resource_group_name and managed_image_name." ) )
2016-05-18 20:25:57 -04:00
}
2017-06-07 18:01:10 -04:00
if c . CaptureContainerName != "" {
if ! reCaptureContainerName . MatchString ( c . CaptureContainerName ) {
errs = packer . MultiErrorAppend ( errs , fmt . Errorf ( "A capture_container_name must satisfy the regular expression %q." , reCaptureContainerName . String ( ) ) )
}
2016-03-04 05:14:55 -05:00
2017-06-07 18:01:10 -04:00
if strings . HasSuffix ( c . CaptureContainerName , "-" ) {
errs = packer . MultiErrorAppend ( errs , fmt . Errorf ( "A capture_container_name must not end with a hyphen, e.g. '-'." ) )
}
2016-05-18 20:25:57 -04:00
2017-06-07 18:01:10 -04:00
if strings . Contains ( c . CaptureContainerName , "--" ) {
errs = packer . MultiErrorAppend ( errs , fmt . Errorf ( "A capture_container_name must not contain consecutive hyphens, e.g. '--'." ) )
}
2016-05-18 20:25:57 -04:00
2017-06-07 18:01:10 -04:00
if c . CaptureNamePrefix == "" {
errs = packer . MultiErrorAppend ( errs , fmt . Errorf ( "A capture_name_prefix must be specified" ) )
}
if ! reCaptureNamePrefix . MatchString ( c . CaptureNamePrefix ) {
errs = packer . MultiErrorAppend ( errs , fmt . Errorf ( "A capture_name_prefix must satisfy the regular expression %q." , reCaptureNamePrefix . String ( ) ) )
}
if strings . HasSuffix ( c . CaptureNamePrefix , "-" ) || strings . HasSuffix ( c . CaptureNamePrefix , "." ) {
errs = packer . MultiErrorAppend ( errs , fmt . Errorf ( "A capture_name_prefix must not end with a hyphen or period." ) )
}
2016-03-04 05:14:55 -05:00
}
2017-11-16 19:34:13 -05:00
if c . TempResourceGroupName != "" && c . BuildResourceGroupName != "" {
errs = packer . MultiErrorAppend ( errs , fmt . Errorf ( "The settings temp_resource_group_name and build_resource_group_name cannot both be defined. Please define one or neither." ) )
}
2016-03-04 05:14:55 -05:00
/////////////////////////////////////////////
// Compute
2017-06-07 18:01:10 -04:00
toInt := func ( b bool ) int {
if b {
return 1
} else {
return 0
}
}
isImageUrl := c . ImageUrl != ""
isCustomManagedImage := c . CustomManagedImageName != "" || c . CustomManagedImageResourceGroupName != ""
2018-10-09 16:56:49 -04:00
isSharedGallery := c . SharedGallery . GalleryName != ""
2017-06-07 18:01:10 -04:00
isPlatformImage := c . ImagePublisher != "" || c . ImageOffer != "" || c . ImageSku != ""
2018-10-03 19:10:46 -04:00
countSourceInputs := toInt ( isImageUrl ) + toInt ( isCustomManagedImage ) + toInt ( isPlatformImage ) + toInt ( isSharedGallery )
2017-06-07 18:01:10 -04:00
if countSourceInputs > 1 {
2018-10-09 16:56:49 -04:00
errs = packer . MultiErrorAppend ( errs , fmt . Errorf ( "Specify either a VHD (image_url), Image Reference (image_publisher, image_offer, image_sku), a Managed Disk (custom_managed_disk_image_name, custom_managed_disk_resource_group_name), or a Shared Gallery Image (shared_image_gallery)" ) )
2017-05-29 00:06:09 -04:00
}
2017-08-13 03:27:51 -04:00
if isImageUrl && c . ManagedImageResourceGroupName != "" {
errs = packer . MultiErrorAppend ( errs , fmt . Errorf ( "A managed image must be created from a managed image, it cannot be created from a VHD." ) )
}
2018-10-09 16:56:49 -04:00
if c . SharedGallery . GalleryName != "" {
if c . SharedGallery . Subscription == "" {
errs = packer . MultiErrorAppend ( errs , fmt . Errorf ( "A shared_image_gallery.subscription must be specified" ) )
}
if c . SharedGallery . ResourceGroup == "" {
errs = packer . MultiErrorAppend ( errs , fmt . Errorf ( "A shared_image_gallery.resource_group must be specified" ) )
}
if c . SharedGallery . ImageName == "" {
errs = packer . MultiErrorAppend ( errs , fmt . Errorf ( "A shared_image_gallery.image_name must be specified" ) )
2018-10-03 19:10:46 -04:00
}
2018-10-09 16:56:49 -04:00
if c . CaptureContainerName != "" {
errs = packer . MultiErrorAppend ( errs , fmt . Errorf ( "VHD Target [capture_container_name] is not supported when using Shared Image Gallery as source. Use managed_image_resource_group_name instead." ) )
2018-10-03 19:10:46 -04:00
}
2018-10-09 16:56:49 -04:00
if c . CaptureNamePrefix != "" {
errs = packer . MultiErrorAppend ( errs , fmt . Errorf ( "VHD Target [capture_name_prefix] is not supported when using Shared Image Gallery as source. Use managed_image_name instead." ) )
2018-10-03 19:10:46 -04:00
}
} else if c . ImageUrl == "" && c . CustomManagedImageName == "" {
2016-05-21 02:01:16 -04:00
if c . ImagePublisher == "" {
errs = packer . MultiErrorAppend ( errs , fmt . Errorf ( "An image_publisher must be specified" ) )
}
if c . ImageOffer == "" {
errs = packer . MultiErrorAppend ( errs , fmt . Errorf ( "An image_offer must be specified" ) )
}
if c . ImageSku == "" {
errs = packer . MultiErrorAppend ( errs , fmt . Errorf ( "An image_sku must be specified" ) )
}
2017-05-29 00:06:09 -04:00
} else if c . ImageUrl == "" && c . ImagePublisher == "" {
2017-05-30 14:25:46 -04:00
if c . CustomManagedImageResourceGroupName == "" {
2019-06-03 20:47:29 -04:00
errs = packer . MultiErrorAppend ( errs , fmt . Errorf ( "A custom_managed_image_resource_group_name must be specified" ) )
2017-05-30 14:25:46 -04:00
}
if c . CustomManagedImageName == "" {
errs = packer . MultiErrorAppend ( errs , fmt . Errorf ( "A custom_managed_image_name must be specified" ) )
2017-05-29 00:06:09 -04:00
}
if c . ManagedImageResourceGroupName == "" {
2019-06-03 20:47:29 -04:00
errs = packer . MultiErrorAppend ( errs , fmt . Errorf ( "A managed_image_resource_group_name must be specified" ) )
2017-05-29 00:06:09 -04:00
}
2017-05-30 14:25:46 -04:00
if c . ManagedImageName == "" {
2019-06-03 20:47:29 -04:00
errs = packer . MultiErrorAppend ( errs , fmt . Errorf ( "A managed_image_name must be specified" ) )
2017-05-29 00:06:09 -04:00
}
2016-05-21 02:01:16 -04:00
} else {
if c . ImagePublisher != "" || c . ImageOffer != "" || c . ImageSku != "" || c . ImageVersion != "" {
errs = packer . MultiErrorAppend ( errs , fmt . Errorf ( "An image_url must not be specified if image_publisher, image_offer, image_sku, or image_version is specified" ) )
}
2016-03-04 05:14:55 -05:00
}
/////////////////////////////////////////////
// Deployment
2017-06-07 18:01:10 -04:00
xor := func ( a , b bool ) bool {
return ( a || b ) && ! ( a && b )
2016-03-04 05:14:55 -05:00
}
2017-06-07 18:01:10 -04:00
if ! xor ( ( c . StorageAccount != "" || c . ResourceGroupName != "" ) , ( c . ManagedImageName != "" || c . ManagedImageResourceGroupName != "" ) ) {
errs = packer . MultiErrorAppend ( errs , fmt . Errorf ( "Specify either a VHD (storage_account and resource_group_name) or Managed Image (managed_image_resource_group_name and managed_image_name) output" ) )
2016-06-09 04:00:23 -04:00
}
2017-06-07 18:01:10 -04:00
2017-11-30 19:43:35 -05:00
if ! xor ( c . Location != "" , c . BuildResourceGroupName != "" ) {
2018-03-05 00:21:02 -05:00
errs = packer . MultiErrorAppend ( errs , fmt . Errorf ( "Specify either a location to create the resource group in or an existing build_resource_group_name, but not both." ) )
2017-11-30 19:43:35 -05:00
}
2017-06-07 18:01:10 -04:00
if c . ManagedImageName == "" && c . ManagedImageResourceGroupName == "" {
if c . StorageAccount == "" {
errs = packer . MultiErrorAppend ( errs , fmt . Errorf ( "A storage_account must be specified" ) )
}
if c . ResourceGroupName == "" {
errs = packer . MultiErrorAppend ( errs , fmt . Errorf ( "A resource_group_name must be specified" ) )
}
}
2017-11-17 12:49:23 -05:00
if c . TempResourceGroupName != "" {
if ok , err := assertResourceGroupName ( c . TempResourceGroupName , "temp_resource_group_name" ) ; ! ok {
errs = packer . MultiErrorAppend ( errs , err )
}
}
if c . BuildResourceGroupName != "" {
if ok , err := assertResourceGroupName ( c . BuildResourceGroupName , "build_resource_group_name" ) ; ! ok {
errs = packer . MultiErrorAppend ( errs , err )
}
}
if c . ManagedImageResourceGroupName != "" {
if ok , err := assertResourceGroupName ( c . ManagedImageResourceGroupName , "managed_image_resource_group_name" ) ; ! ok {
errs = packer . MultiErrorAppend ( errs , err )
}
}
if c . ManagedImageName != "" {
if ok , err := assertManagedImageName ( c . ManagedImageName , "managed_image_name" ) ; ! ok {
errs = packer . MultiErrorAppend ( errs , err )
}
}
2019-06-20 22:47:39 -04:00
if c . ManagedImageName != "" && c . ManagedImageResourceGroupName != "" && c . SharedGalleryDestination . SigDestinationGalleryName != "" {
2019-06-03 20:47:29 -04:00
if c . SharedGalleryDestination . SigDestinationResourceGroup == "" {
errs = packer . MultiErrorAppend ( errs , fmt . Errorf ( "A resource_group must be specified for shared_image_gallery_destination" ) )
}
if c . SharedGalleryDestination . SigDestinationImageName == "" {
errs = packer . MultiErrorAppend ( errs , fmt . Errorf ( "An image_name must be specified for shared_image_gallery_destination" ) )
}
if c . SharedGalleryDestination . SigDestinationImageVersion == "" {
errs = packer . MultiErrorAppend ( errs , fmt . Errorf ( "An image_version must be specified for shared_image_gallery_destination" ) )
}
if len ( c . SharedGalleryDestination . SigDestinationReplicationRegions ) == 0 {
errs = packer . MultiErrorAppend ( errs , fmt . Errorf ( "A list of replication_regions must be specified for shared_image_gallery_destination" ) )
}
}
2019-07-26 18:20:32 -04:00
if c . SharedGalleryTimeout == 0 {
// default to a one-hour timeout. In the sdk, the default is 15 m.
c . SharedGalleryTimeout = 60 * time . Minute
}
2019-06-03 20:47:29 -04:00
2018-11-05 18:48:22 -05:00
if c . ManagedImageOSDiskSnapshotName != "" {
2018-11-06 14:17:03 -05:00
if ok , err := assertManagedImageOSDiskSnapshotName ( c . ManagedImageOSDiskSnapshotName , "managed_image_os_disk_snapshot_name" ) ; ! ok {
2018-11-05 18:48:22 -05:00
errs = packer . MultiErrorAppend ( errs , err )
}
}
if c . ManagedImageDataDiskSnapshotPrefix != "" {
2018-11-06 14:17:03 -05:00
if ok , err := assertManagedImageDataDiskSnapshotName ( c . ManagedImageDataDiskSnapshotPrefix , "managed_image_data_disk_snapshot_prefix" ) ; ! ok {
2018-11-05 18:48:22 -05:00
errs = packer . MultiErrorAppend ( errs , err )
}
}
2016-06-30 19:51:52 -04:00
if c . VirtualNetworkName == "" && c . VirtualNetworkResourceGroupName != "" {
errs = packer . MultiErrorAppend ( errs , fmt . Errorf ( "If virtual_network_resource_group_name is specified, so must virtual_network_name" ) )
}
if c . VirtualNetworkName == "" && c . VirtualNetworkSubnetName != "" {
errs = packer . MultiErrorAppend ( errs , fmt . Errorf ( "If virtual_network_subnet_name is specified, so must virtual_network_name" ) )
}
2016-04-21 19:50:03 -04:00
2019-09-24 12:34:19 -04:00
if c . AllowedInboundIpAddresses != nil && len ( c . AllowedInboundIpAddresses ) >= 1 {
2019-09-26 18:35:55 -04:00
if c . VirtualNetworkName != "" {
errs = packer . MultiErrorAppend ( errs , fmt . Errorf ( "If virtual_network_name is specified, allowed_inbound_ip_addresses cannot be specified" ) )
} else {
if ok , err := assertAllowedInboundIpAddresses ( c . AllowedInboundIpAddresses , "allowed_inbound_ip_addresses" ) ; ! ok {
errs = packer . MultiErrorAppend ( errs , err )
}
2019-09-24 12:34:19 -04:00
}
}
2018-03-05 04:27:52 -05:00
/////////////////////////////////////////////
// Plan Info
2018-03-09 01:39:23 -05:00
if c . PlanInfo . PlanName != "" || c . PlanInfo . PlanProduct != "" || c . PlanInfo . PlanPublisher != "" || c . PlanInfo . PlanPromotionCode != "" {
if c . PlanInfo . PlanName == "" || c . PlanInfo . PlanProduct == "" || c . PlanInfo . PlanPublisher == "" {
2018-03-05 04:27:52 -05:00
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" ) )
2018-03-09 01:39:23 -05:00
} 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
2018-03-05 04:27:52 -05:00
}
}
2019-10-11 18:29:02 -04:00
/////////////////////////////////////////////
// Polling Duration Timeout
if c . PollingDurationTimeout == 0 {
// In the sdk, the default is 15 m.
c . PollingDurationTimeout = 15 * time . Minute
}
2016-04-21 19:50:03 -04:00
/////////////////////////////////////////////
// OS
2016-10-12 20:54:59 -04:00
if strings . EqualFold ( c . OSType , constants . Target_Linux ) {
c . OSType = constants . Target_Linux
} else if strings . EqualFold ( c . OSType , constants . Target_Windows ) {
c . OSType = constants . Target_Windows
} else if c . OSType == "" {
2016-04-21 19:50:03 -04:00
errs = packer . MultiErrorAppend ( errs , fmt . Errorf ( "An os_type must be specified" ) )
2016-10-12 20:54:59 -04:00
} else {
errs = packer . MultiErrorAppend ( errs , fmt . Errorf ( "The os_type %q is invalid" , c . OSType ) )
2016-04-21 19:50:03 -04:00
}
2017-08-13 04:45:04 -04:00
switch c . ManagedImageStorageAccountType {
2018-04-06 04:12:58 -04:00
case "" , string ( compute . StorageAccountTypesStandardLRS ) :
c . managedImageStorageAccountType = compute . StorageAccountTypesStandardLRS
case string ( compute . StorageAccountTypesPremiumLRS ) :
c . managedImageStorageAccountType = compute . StorageAccountTypesPremiumLRS
2017-08-13 04:45:04 -04:00
default :
errs = packer . MultiErrorAppend ( errs , fmt . Errorf ( "The managed_image_storage_account_type %q is invalid" , c . ManagedImageStorageAccountType ) )
}
2018-11-30 15:32:41 -05:00
switch c . DiskCachingType {
case string ( compute . CachingTypesNone ) :
c . diskCachingType = compute . CachingTypesNone
case string ( compute . CachingTypesReadOnly ) :
c . diskCachingType = compute . CachingTypesReadOnly
case "" , string ( compute . CachingTypesReadWrite ) :
c . diskCachingType = compute . CachingTypesReadWrite
default :
errs = packer . MultiErrorAppend ( errs , fmt . Errorf ( "The disk_caching_type %q is invalid" , c . DiskCachingType ) )
}
2016-03-04 05:14:55 -05:00
}
2017-11-17 12:49:23 -05:00
func assertManagedImageName ( name , setting string ) ( bool , error ) {
if ! isValidAzureName ( reManagedDiskName , name ) {
return false , fmt . Errorf ( "The setting %s must match the regular expression %q, and not end with a '-' or '.'." , setting , validManagedDiskName )
}
return true , nil
}
2018-11-06 14:17:03 -05:00
func assertManagedImageOSDiskSnapshotName ( name , setting string ) ( bool , error ) {
2018-11-07 17:23:22 -05:00
if ! isValidAzureName ( reSnapshotName , name ) {
2018-11-05 18:48:22 -05:00
return false , fmt . Errorf ( "The setting %s must only contain characters from a-z, A-Z, 0-9 and _ and the maximum length is 80 characters" , setting )
}
return true , nil
}
2018-11-06 14:17:03 -05:00
func assertManagedImageDataDiskSnapshotName ( name , setting string ) ( bool , error ) {
2018-11-07 17:23:22 -05:00
if ! isValidAzureName ( reSnapshotPrefix , name ) {
2018-11-06 14:17:03 -05:00
return false , fmt . Errorf ( "The setting %s must only contain characters from a-z, A-Z, 0-9 and _ and the maximum length (excluding the prefix) is 60 characters" , setting )
}
return true , nil
}
2019-09-24 12:34:19 -04:00
func assertAllowedInboundIpAddresses ( ipAddresses [ ] string , setting string ) ( bool , error ) {
for _ , ipAddress := range ipAddresses {
if net . ParseIP ( ipAddress ) == nil {
if _ , _ , err := net . ParseCIDR ( ipAddress ) ; err != nil {
return false , fmt . Errorf ( "The setting %s must only contain valid IP addresses or CIDR blocks" , setting )
}
}
}
return true , nil
}
2017-11-17 12:49:23 -05:00
func assertResourceGroupName ( rgn , setting string ) ( bool , error ) {
if ! isValidAzureName ( reResourceGroupName , rgn ) {
return false , fmt . Errorf ( "The setting %s must match the regular expression %q, and not end with a '-' or '.'." , setting , validResourceGroupNameRe )
}
return true , nil
}
func isValidAzureName ( re * regexp . Regexp , rgn string ) bool {
return re . Match ( [ ] byte ( rgn ) ) &&
! strings . HasSuffix ( rgn , "." ) &&
! strings . HasSuffix ( rgn , "-" )
}
2019-02-15 10:24:19 -05:00
2020-03-24 09:43:24 -04:00
// The supplied password must be between 8-123 characters long and must satisfy at least 3 of password complexity requirements from the following:
// 1) Contains an uppercase character
// 2) Contains a lowercase character
// 3) Contains a numeric digit
// 4) Contains a special character
// 5) Control characters are not allowed (a very specific case - not included in this validation)
func isValidPassword ( password string ) bool {
if ! ( len ( password ) >= 8 && len ( password ) <= 123 ) {
return false
}
requirements := 0
if strings . ContainsAny ( password , random . PossibleNumbers ) {
requirements ++
}
if strings . ContainsAny ( password , random . PossibleLowerCase ) {
requirements ++
}
if strings . ContainsAny ( password , random . PossibleUpperCase ) {
requirements ++
}
if strings . ContainsAny ( password , random . PossibleSpecialCharacter ) {
requirements ++
}
return requirements >= 3
}
2019-02-15 10:24:19 -05:00
func ( c * Config ) validateLocationZoneResiliency ( say func ( s string ) ) {
// Docs on regions that support Availibility Zones:
// https://docs.microsoft.com/en-us/azure/availability-zones/az-overview#regions-that-support-availability-zones
// Query technical names for locations:
// az account list-locations --query '[].name' -o tsv
var zones = make ( map [ string ] struct { } )
zones [ "westeurope" ] = struct { } { }
zones [ "centralus" ] = struct { } { }
zones [ "eastus2" ] = struct { } { }
zones [ "francecentral" ] = struct { } { }
zones [ "northeurope" ] = struct { } { }
zones [ "southeastasia" ] = struct { } { }
zones [ "westus2" ] = struct { } { }
if _ , ok := zones [ c . Location ] ; ! ok {
say ( fmt . Sprintf ( "WARNING: Zone resiliency may not be supported in %s, checkout the docs at https://docs.microsoft.com/en-us/azure/availability-zones/" , c . Location ) )
}
}