diff --git a/builder/azure/arm/builder.go b/builder/azure/arm/builder.go index 799659c50..1bd63f426 100644 --- a/builder/azure/arm/builder.go +++ b/builder/azure/arm/builder.go @@ -231,8 +231,16 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack keyVaultDeploymentName := b.stateBag.Get(constants.ArmKeyVaultDeploymentName).(string) steps = []multistep.Step{ NewStepCreateResourceGroup(azureClient, ui), - NewStepValidateTemplate(azureClient, ui, &b.config, GetKeyVaultDeployment), - NewStepDeployTemplate(azureClient, ui, &b.config, keyVaultDeploymentName, GetKeyVaultDeployment), + } + if b.config.BuildKeyVaultName == "" { + steps = append(steps, + NewStepValidateTemplate(azureClient, ui, &b.config, GetKeyVaultDeployment), + NewStepDeployTemplate(azureClient, ui, &b.config, keyVaultDeploymentName, GetKeyVaultDeployment), + ) + } else { + steps = append(steps, NewStepCertificateInKeyVault(azureClient, ui, &b.config)) + } + steps = append(steps, NewStepGetCertificate(azureClient, ui), NewStepSetCertificate(&b.config, ui), NewStepValidateTemplate(azureClient, ui, &b.config, GetVirtualMachineDeployment), @@ -261,7 +269,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack NewStepDeleteResourceGroup(azureClient, ui), NewStepDeleteOSDisk(azureClient, ui), NewStepDeleteAdditionalDisks(azureClient, ui), - } + ) } else { return nil, fmt.Errorf("Builder does not support the os_type '%s'", b.config.OSType) } @@ -395,7 +403,14 @@ func (b *Builder) configureStateBag(stateBag multistep.StateBag) { stateBag.Put(constants.ArmKeyVaultDeploymentName, fmt.Sprintf("kv%s", b.config.tmpDeploymentName)) } - stateBag.Put(constants.ArmKeyVaultName, b.config.tmpKeyVaultName) + if b.config.BuildKeyVaultName != "" { + stateBag.Put(constants.ArmKeyVaultName, b.config.BuildKeyVaultName) + b.config.tmpKeyVaultName = b.config.BuildKeyVaultName + stateBag.Put(constants.ArmIsExistingKeyVault, false) + } else { + stateBag.Put(constants.ArmKeyVaultName, b.config.tmpKeyVaultName) + stateBag.Put(constants.ArmIsExistingKeyVault, true) + } stateBag.Put(constants.ArmNicName, b.config.tmpNicName) stateBag.Put(constants.ArmPublicIPAddressName, b.config.tmpPublicIPAddressName) stateBag.Put(constants.ArmResourceGroupName, b.config.BuildResourceGroupName) diff --git a/builder/azure/arm/config.go b/builder/azure/arm/config.go index 18b426e19..3b701eae7 100644 --- a/builder/azure/arm/config.go +++ b/builder/azure/arm/config.go @@ -253,7 +253,10 @@ type Config struct { // 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. - BuildResourceGroupName string `mapstructure:"build_resource_group_name"` + BuildResourceGroupName string `mapstructure:"build_resource_group_name"` + // Specify an existing key vault to use for uploading certificates to the + // instance to connect. + BuildKeyVaultName string `mapstructure:"build_key_vault_name"` storageAccountBlobEndpoint string // This value allows you to // set a virtual_network_name and obtain a public IP. If this value is not diff --git a/builder/azure/arm/step_certificate_in_keyvault.go b/builder/azure/arm/step_certificate_in_keyvault.go new file mode 100644 index 000000000..a46b160ce --- /dev/null +++ b/builder/azure/arm/step_certificate_in_keyvault.go @@ -0,0 +1,50 @@ +package arm + +import ( + "context" + "fmt" + + "github.com/hashicorp/packer/builder/azure/common/constants" + "github.com/hashicorp/packer/helper/multistep" + "github.com/hashicorp/packer/packer" +) + +type StepCertificateInKeyVault struct { + config *Config + client *AzureClient + say func(message string) + error func(e error) +} + +func NewStepCertificateInKeyVault(cli *AzureClient, ui packer.Ui, config *Config) *StepCertificateInKeyVault { + var step = &StepCertificateInKeyVault{ + client: cli, + config: config, + say: func(message string) { ui.Say(message) }, + error: func(e error) { ui.Error(e.Error()) }, + } + + return step +} + +func (s *StepCertificateInKeyVault) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { + s.say("Setting the certificate in the KeyVault...") + + var keyVaultName = state.Get(constants.ArmKeyVaultName).(string) + // err := s.client.CreateKey(keyVaultName, DefaultSecretName) + // if err != nil { + // s.error(fmt.Errorf("Error setting winrm cert in custom keyvault: %s", err)) + // return multistep.ActionHalt + // } + + err := s.client.SetSecret(keyVaultName, DefaultSecretName, s.config.winrmCertificate) + if err != nil { + s.error(fmt.Errorf("Error setting winrm cert in custom keyvault: %s", err)) + return multistep.ActionHalt + } + + return multistep.ActionContinue +} + +func (*StepCertificateInKeyVault) Cleanup(multistep.StateBag) { +} diff --git a/builder/azure/arm/step_delete_resource_group.go b/builder/azure/arm/step_delete_resource_group.go index aac1c4b91..80564b1c6 100644 --- a/builder/azure/arm/step_delete_resource_group.go +++ b/builder/azure/arm/step_delete_resource_group.go @@ -44,9 +44,12 @@ func (s *StepDeleteResourceGroup) deleteResourceGroup(ctx context.Context, state } if keyVaultDeploymentName, ok := state.GetOk(constants.ArmKeyVaultDeploymentName); ok { - err = s.deleteDeploymentResources(ctx, keyVaultDeploymentName.(string), resourceGroupName) - if err != nil { - return err + // Only delete if custom keyvault was not provided. + if exists := state.Get(constants.ArmIsExistingKeyVault).(bool); exists { + err = s.deleteDeploymentResources(ctx, keyVaultDeploymentName.(string), resourceGroupName) + if err != nil { + return err + } } } diff --git a/builder/azure/arm/step_deploy_template.go b/builder/azure/arm/step_deploy_template.go index a8626bd00..15d19394f 100644 --- a/builder/azure/arm/step_deploy_template.go +++ b/builder/azure/arm/step_deploy_template.go @@ -76,6 +76,10 @@ func (s *StepDeployTemplate) deleteTemplate(ctx context.Context, state multistep } func (s *StepDeployTemplate) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { + if s.config.BuildKeyVaultName != "" { + // Deployment already exists + + } s.say("Deploying deployment template ...") var resourceGroupName = state.Get(constants.ArmResourceGroupName).(string) diff --git a/builder/azure/common/constants/stateBag.go b/builder/azure/common/constants/stateBag.go index 0bf0ad1ae..b18e5f152 100644 --- a/builder/azure/common/constants/stateBag.go +++ b/builder/azure/common/constants/stateBag.go @@ -36,6 +36,7 @@ const ( ArmTags string = "arm.Tags" ArmVirtualMachineCaptureParameters string = "arm.VirtualMachineCaptureParameters" ArmIsExistingResourceGroup string = "arm.IsExistingResourceGroup" + ArmIsExistingKeyVault string = "arm.IsExistingKeyVault" ArmIsManagedImage string = "arm.IsManagedImage" ArmManagedImageResourceGroupName string = "arm.ManagedImageResourceGroupName" diff --git a/builder/azure/common/vault.go b/builder/azure/common/vault.go index 1bda604a2..3c53c0074 100644 --- a/builder/azure/common/vault.go +++ b/builder/azure/common/vault.go @@ -54,7 +54,8 @@ func (client *VaultClient) GetSecret(vaultName, secretName string) (*Secret, err autorest.AsGet(), autorest.WithBaseURL(client.getVaultUrl(vaultName)), autorest.WithPathParameters("/secrets/{secret-name}", p), - autorest.WithQueryParameters(q)) + autorest.WithQueryParameters(q), + ) if err != nil { return nil, err @@ -86,6 +87,47 @@ func (client *VaultClient) GetSecret(vaultName, secretName string) (*Secret, err return &secret, nil } +func (client *VaultClient) SetSecret(vaultName, secretName string, secretValue string) error { + p := map[string]interface{}{ + "secret-name": autorest.Encode("path", secretName), + } + q := map[string]interface{}{ + "api-version": AzureVaultApiVersion, + } + + jsonBody := fmt.Sprintf(`{"value": "%s"}`, secretValue) + + req, err := autorest.Prepare( + &http.Request{}, + autorest.AsPut(), + autorest.AsContentType("application/json; charset=utf-8"), + autorest.WithBaseURL(client.getVaultUrl(vaultName)), + autorest.WithPathParameters("/secrets/{secret-name}", p), + autorest.WithQueryParameters(q), + autorest.WithString(jsonBody), + ) + + if err != nil { + return err + } + + resp, err := autorest.SendWithSender(client, req) + if err != nil { + return err + } + + if resp.StatusCode != 200 { + return fmt.Errorf( + "Failed to set secret to %s/%s, HTTP status code=%d (%s)", + vaultName, + secretName, + resp.StatusCode, + http.StatusText(resp.StatusCode)) + } + + return nil +} + // Delete deletes the specified Azure key vault. // // resourceGroupName is the name of the Resource Group to which the vault belongs. vaultName is the name of the vault