Skip to content

Commit

Permalink
Add support for setting public IP sku to ARM builder
Browse files Browse the repository at this point in the history
  • Loading branch information
JenGoldstrich committed May 16, 2024
1 parent 3a89a4e commit b7716c6
Show file tree
Hide file tree
Showing 14 changed files with 410 additions and 3 deletions.
2 changes: 2 additions & 0 deletions .web-docs/components/builder/arm/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -538,6 +538,8 @@ Providing `temp_resource_group_name` or `location` in combination with
- `encryption_at_host` (\*bool) - Specifies if Encryption at host is enabled for the Virtual Machine.
Requires enabling encryption at host in the Subscription read more [here](https://learn.microsoft.com/en-us/azure/virtual-machines/disks-enable-host-based-encryption-portal?tabs=azure-powershell)

- `public_ip_sku` (string) - Public Ip SKU

- `vtpm_enabled` (bool) - Specifies if vTPM (virtual Trusted Platform Module) is enabled for the Virtual Machine. For Trusted Launch or Confidential VMs, vTPM must be enabled.

- `security_type` (string) - Specifies the type of security to use for the VM. "TrustedLaunch" or "ConfidentialVM"
Expand Down
4 changes: 4 additions & 0 deletions builder/azure/arm/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,10 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
if b.config.ClientConfig.ObjectID == "" && b.config.OSType != constants.Target_Linux {
return nil, fmt.Errorf("could not determine the ObjectID for the user, which is required for Windows builds")
}
publicIPWarning := "On 31 March 2025 Azure is disabling the ability to create public IP Addresses with the Basic SKU, this builder has always created a basic SKU public IP Address to connect to the build VM. We reccomend setting the `public_ip_sku` field to `Standard`, this will make sure that your build is created with a standard public IPs, and help ensure any issues with this change are caught before the removal of Basic SKU public IPs. At that point this plugin will be updated to default to Standard IP skus. You can read more about this in the official Azure announcement https://azure.microsoft.com/en-us/updates/upgrade-to-standard-sku-public-ip-addresses-in-azure-by-30-september-2025-basic-sku-will-be-retired/"
if b.config.PublicIpSKU == "" || b.config.PublicIpSKU == constants.BasicPublicIPSKU {
ui.Message(publicIPWarning)
}

if b.config.isManagedImage() {
groupId := commonids.NewResourceGroupID(b.config.ClientConfig.SubscriptionID, b.config.ManagedImageResourceGroupName)
Expand Down
18 changes: 18 additions & 0 deletions builder/azure/arm/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -623,6 +623,8 @@ type Config struct {
// Requires enabling encryption at host in the Subscription read more [here](https://learn.microsoft.com/en-us/azure/virtual-machines/disks-enable-host-based-encryption-portal?tabs=azure-powershell)
EncryptionAtHost *bool `mapstructure:"encryption_at_host" required:"false"`

PublicIpSKU string `mapstructure:"public_ip_sku" required:"false"`

// Specifies if vTPM (virtual Trusted Platform Module) is enabled for the Virtual Machine. For Trusted Launch or Confidential VMs, vTPM must be enabled.
VTpmEnabled bool `mapstructure:"vtpm_enabled" required:"false"`

Expand Down Expand Up @@ -1412,6 +1414,11 @@ func assertRequiredParametersSet(c *Config, errs *packersdk.MultiError) {
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("If virtual_network_subnet_name is specified, so must virtual_network_name"))
}

if c.PublicIpSKU != "" {
if ok, err := assertAllowedPublicIPSkuType(c.PublicIpSKU); !ok {
errs = packersdk.MultiErrorAppend(errs, err)
}
}
if c.AllowedInboundIpAddresses != nil && len(c.AllowedInboundIpAddresses) >= 1 {
if c.VirtualNetworkName != "" {
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("If virtual_network_name is specified, allowed_inbound_ip_addresses cannot be specified"))
Expand Down Expand Up @@ -1595,6 +1602,17 @@ func assertResourceGroupName(rgn, setting string) (bool, error) {
return true, nil
}

func assertAllowedPublicIPSkuType(publicIpSku string) (bool, error) {
switch publicIpSku {
case constants.BasicPublicIPSKU:
return true, nil
case constants.StandardPublicIPSKU:
return true, nil
default:
return false, fmt.Errorf("The %s %q must match either %q or %q", "public_ip_sku", publicIpSku, constants.BasicPublicIPSKU, constants.StandardPublicIPSKU)
}
}

func assertAllowedSecurityType(securityType string) (bool, error) {
switch securityType {
case constants.TrustedLaunch:
Expand Down
2 changes: 2 additions & 0 deletions builder/azure/arm/config.hcl2spec.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

84 changes: 84 additions & 0 deletions builder/azure/arm/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3408,3 +3408,87 @@ func TestConfigShouldRejectIfNoDiskEncryptionIDIsSetInSIGTargetRegions(t *testin
t.Errorf("expected config to reject with error containing %s but got %s", errorMessageRegion2, err)
}
}

func TestConfigShouldRejectInvalidIPSku(t *testing.T) {
config := map[string]interface{}{
"image_offer": "ignore",
"image_publisher": "ignore",
"image_sku": "ignore",
"location": "ignore",
"subscription_id": "ignore",
"communicator": "none",
"public_ip_sku": "invalid",
"os_type": constants.Target_Linux,
"security_type": "ConfidentialVM",
"security_encryption_type": "DiskWithVMGuestState",
"disk_encryption_set_id": "ignore",
"shared_image_gallery_destination": map[string]interface{}{
"resource_group": "ignore",
"gallery_name": "ignore",
"image_name": "ignore",
"image_version": "1.0.1",
},
}
errorMessageInvalidPublicIPSku := `The public_ip_sku "invalid" must match either "Basic" or "Standard`
var c Config
_, err := c.Prepare(config, getPackerConfiguration())
if err == nil {
t.Fatalf("expected config to reject with the following error: %q",
errorMessageInvalidPublicIPSku,
)
}
if !strings.Contains(err.Error(), errorMessageInvalidPublicIPSku) {
t.Errorf("expected config to reject with error containing %s but got %s", errorMessageInvalidPublicIPSku, err)
}
}

func TestConfigShouldAcceptValidIPSkus(t *testing.T) {
basicConfig := map[string]interface{}{
"image_offer": "ignore",
"image_publisher": "ignore",
"image_sku": "ignore",
"location": "ignore",
"subscription_id": "ignore",
"communicator": "none",
"public_ip_sku": "Basic",
"os_type": constants.Target_Linux,
"security_type": "ConfidentialVM",
"security_encryption_type": "DiskWithVMGuestState",
"disk_encryption_set_id": "ignore",
"shared_image_gallery_destination": map[string]interface{}{
"resource_group": "ignore",
"gallery_name": "ignore",
"image_name": "ignore",
"image_version": "1.0.1",
},
}
standardConfig := map[string]interface{}{
"image_offer": "ignore",
"image_publisher": "ignore",
"image_sku": "ignore",
"location": "ignore",
"subscription_id": "ignore",
"communicator": "none",
"public_ip_sku": "Standard",
"os_type": constants.Target_Linux,
"security_type": "ConfidentialVM",
"security_encryption_type": "DiskWithVMGuestState",
"disk_encryption_set_id": "ignore",
"shared_image_gallery_destination": map[string]interface{}{
"resource_group": "ignore",
"gallery_name": "ignore",
"image_name": "ignore",
"image_version": "1.0.1",
},
}
var c Config
_, err := c.Prepare(basicConfig, getPackerConfiguration())
if err != nil {
t.Fatalf("expected config to not reject basic IP sku but rejected with error %s", err)
}
_, err = c.Prepare(standardConfig, getPackerConfiguration())
if err != nil {
t.Fatalf("expected config to not reject standard IP sku but rejected with error %s", err)
}

}
1 change: 1 addition & 0 deletions builder/azure/arm/step_deploy_template.go
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,7 @@ func (s *StepDeployTemplate) deployTemplate(ctx context.Context, subscriptionId
if err != nil {
return err
}
s.say(fmt.Sprintf("%v", *deployment.Properties.Template))
pollingContext, cancel := context.WithTimeout(ctx, s.client.PollingDuration)
defer cancel()
id := deployments.NewResourceGroupProviderDeploymentID(subscriptionId, resourceGroupName, deploymentName)
Expand Down
12 changes: 11 additions & 1 deletion builder/azure/arm/template_factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -320,13 +320,23 @@ func GetVirtualMachineTemplateBuilder(config *Config) (*template.TemplateBuilder
}
}

if config.AllowedInboundIpAddresses != nil && len(config.AllowedInboundIpAddresses) >= 1 && config.Comm.Port() != 0 {
if (config.PublicIpSKU == "Standard") || (config.AllowedInboundIpAddresses != nil && len(config.AllowedInboundIpAddresses) >= 1) {
err = builder.SetNetworkSecurityGroup(config.AllowedInboundIpAddresses, config.Comm.Port())
if err != nil {
return nil, err
}
}

if config.PublicIpSKU == "Standard" {
err = builder.SetPublicIPSKU("Standard", "Regional")
if err != nil {
return nil, err
}
// Standard SKU Public IPs only support static assignment
// Before this the plugin always set this as dynamic
builder.SetPublicIpAllocationMethod("Static")
}

if config.BootDiagSTGAccount != "" {
err = builder.SetBootDiagnostics(config.BootDiagSTGAccount)
if err != nil {
Expand Down
Loading

0 comments on commit b7716c6

Please sign in to comment.