azure: delete keyvault deployment
This commit is contained in:
parent
aa2bbf2aec
commit
da0c13f622
|
@ -48,6 +48,7 @@ type AzureClient struct {
|
|||
InspectorMaxLength int
|
||||
Template *CaptureTemplate
|
||||
LastError azureErrorResponse
|
||||
VaultClientDelete common.VaultClient
|
||||
}
|
||||
|
||||
func getCaptureResponse(body string) *CaptureTemplate {
|
||||
|
@ -209,6 +210,20 @@ func NewAzureClient(subscriptionID, resourceGroupName, storageAccountName string
|
|||
azureClient.VaultClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
||||
azureClient.VaultClient.UserAgent += packerUserAgent
|
||||
|
||||
// TODO(boumenot) - SDK still does not have a full KeyVault client.
|
||||
// There are two ways that KeyVault has to be accessed, and each one has their own SPN. An authenticated SPN
|
||||
// is tied to the URL, and the URL associated with getting the secret is different than the URL
|
||||
// associated with deleting the KeyVault. As a result, I need to have *two* different clients to
|
||||
// access KeyVault. I did not want to split it into two separate files, so I am starting with this.
|
||||
//
|
||||
// I do not like this implementation. It is getting long in the tooth, and should be re-examined now
|
||||
// that we have a "working" solution.
|
||||
azureClient.VaultClientDelete = common.NewVaultClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID)
|
||||
azureClient.VaultClientDelete.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
|
||||
azureClient.VaultClientDelete.RequestInspector = withInspection(maxlen)
|
||||
azureClient.VaultClientDelete.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
||||
azureClient.VaultClientDelete.UserAgent += packerUserAgent
|
||||
|
||||
// If this is a managed disk build, this should be ignored.
|
||||
if resourceGroupName != "" && storageAccountName != "" {
|
||||
accountKeys, err := azureClient.AccountsClient.ListKeys(resourceGroupName, storageAccountName)
|
||||
|
|
|
@ -127,11 +127,13 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
|||
b.setImageParameters(b.stateBag)
|
||||
var steps []multistep.Step
|
||||
|
||||
deploymentName := b.stateBag.Get(constants.ArmDeploymentName).(string)
|
||||
|
||||
if b.config.OSType == constants.Target_Linux {
|
||||
steps = []multistep.Step{
|
||||
NewStepCreateResourceGroup(azureClient, ui),
|
||||
NewStepValidateTemplate(azureClient, ui, b.config, GetVirtualMachineDeployment),
|
||||
NewStepDeployTemplate(azureClient, ui, b.config, GetVirtualMachineDeployment),
|
||||
NewStepDeployTemplate(azureClient, ui, b.config, deploymentName, GetVirtualMachineDeployment),
|
||||
NewStepGetIPAddress(azureClient, ui, endpointConnectType),
|
||||
&communicator.StepConnectSSH{
|
||||
Config: &b.config.Comm,
|
||||
|
@ -146,14 +148,15 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
|||
NewStepDeleteOSDisk(azureClient, ui),
|
||||
}
|
||||
} else if b.config.OSType == constants.Target_Windows {
|
||||
keyVaultDeploymentName := b.stateBag.Get(constants.ArmKeyVaultDeploymentName).(string)
|
||||
steps = []multistep.Step{
|
||||
NewStepCreateResourceGroup(azureClient, ui),
|
||||
NewStepValidateTemplate(azureClient, ui, b.config, GetKeyVaultDeployment),
|
||||
NewStepDeployTemplate(azureClient, ui, b.config, GetKeyVaultDeployment),
|
||||
NewStepDeployTemplate(azureClient, ui, b.config, keyVaultDeploymentName, GetKeyVaultDeployment),
|
||||
NewStepGetCertificate(azureClient, ui),
|
||||
NewStepSetCertificate(b.config, ui),
|
||||
NewStepValidateTemplate(azureClient, ui, b.config, GetVirtualMachineDeployment),
|
||||
NewStepDeployTemplate(azureClient, ui, b.config, GetVirtualMachineDeployment),
|
||||
NewStepDeployTemplate(azureClient, ui, b.config, deploymentName, GetVirtualMachineDeployment),
|
||||
NewStepGetIPAddress(azureClient, ui, endpointConnectType),
|
||||
&communicator.StepConnectWinRM{
|
||||
Config: &b.config.Comm,
|
||||
|
@ -283,6 +286,9 @@ func (b *Builder) configureStateBag(stateBag multistep.StateBag) {
|
|||
stateBag.Put(constants.ArmTags, &b.config.AzureTags)
|
||||
stateBag.Put(constants.ArmComputeName, b.config.tmpComputeName)
|
||||
stateBag.Put(constants.ArmDeploymentName, b.config.tmpDeploymentName)
|
||||
if b.config.OSType == constants.Target_Windows {
|
||||
stateBag.Put(constants.ArmKeyVaultDeploymentName, fmt.Sprintf("kv%s", b.config.tmpDeploymentName))
|
||||
}
|
||||
stateBag.Put(constants.ArmKeyVaultName, b.config.tmpKeyVaultName)
|
||||
stateBag.Put(constants.ArmLocation, b.config.Location)
|
||||
stateBag.Put(constants.ArmNicName, DefaultNicName)
|
||||
|
|
|
@ -37,54 +37,19 @@ func (s *StepDeleteResourceGroup) deleteResourceGroup(state multistep.StateBag,
|
|||
if state.Get(constants.ArmIsExistingResourceGroup).(bool) {
|
||||
s.say("\nThe resource group was not created by Packer, only deleting individual resources ...")
|
||||
var deploymentName = state.Get(constants.ArmDeploymentName).(string)
|
||||
if deploymentName != "" {
|
||||
maxResources := int32(maxResourcesToDelete)
|
||||
deploymentOperations, err := s.client.DeploymentOperationsClient.List(resourceGroupName, deploymentName, &maxResources)
|
||||
err = s.deleteDeploymentResources(deploymentName, resourceGroupName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if keyVaultDeploymentName, ok := state.GetOk(constants.ArmKeyVaultDeploymentName); ok {
|
||||
err = s.deleteDeploymentResources(keyVaultDeploymentName.(string), resourceGroupName)
|
||||
if err != nil {
|
||||
s.say(fmt.Sprintf("Error deleting resources. Please delete them manually.\n\n"+
|
||||
"Name: %s\n"+
|
||||
"Error: %s", resourceGroupName, err))
|
||||
s.error(err)
|
||||
}
|
||||
for _, deploymentOperation := range *deploymentOperations.Value {
|
||||
// Sometimes an empty operation is added to the list by Azure
|
||||
if deploymentOperation.Properties.TargetResource == nil {
|
||||
continue
|
||||
}
|
||||
s.say(fmt.Sprintf(" -> %s : '%s'",
|
||||
*deploymentOperation.Properties.TargetResource.ResourceType,
|
||||
*deploymentOperation.Properties.TargetResource.ResourceName))
|
||||
var networkDeleteFunction func(string, string, <-chan struct{}) (<-chan autorest.Response, <-chan error)
|
||||
switch *deploymentOperation.Properties.TargetResource.ResourceType {
|
||||
case "Microsoft.Compute/virtualMachines":
|
||||
_, errChan := s.client.VirtualMachinesClient.Delete(resourceGroupName, *deploymentOperation.Properties.TargetResource.ResourceName, nil)
|
||||
err := <-errChan
|
||||
if err != nil {
|
||||
s.say(fmt.Sprintf("Error deleting resource. Please delete manually.\n\n"+
|
||||
"Name: %s\n"+
|
||||
"Error: %s", *deploymentOperation.Properties.TargetResource.ResourceName, err.Error()))
|
||||
s.error(err)
|
||||
}
|
||||
case "Microsoft.Network/networkInterfaces":
|
||||
networkDeleteFunction = s.client.InterfacesClient.Delete
|
||||
case "Microsoft.Network/virtualNetworks":
|
||||
networkDeleteFunction = s.client.VirtualNetworksClient.Delete
|
||||
case "Microsoft.Network/publicIPAddresses":
|
||||
networkDeleteFunction = s.client.PublicIPAddressesClient.Delete
|
||||
}
|
||||
if networkDeleteFunction != nil {
|
||||
_, errChan := networkDeleteFunction(resourceGroupName, *deploymentOperation.Properties.TargetResource.ResourceName, nil)
|
||||
err := <-errChan
|
||||
if err != nil {
|
||||
s.say(fmt.Sprintf("Error deleting resource. Please delete manually.\n\n"+
|
||||
"Name: %s\n"+
|
||||
"Error: %s", *deploymentOperation.Properties.TargetResource.ResourceName, err.Error()))
|
||||
s.error(err)
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
return err
|
||||
|
||||
return nil
|
||||
} else {
|
||||
s.say("\nThe resource group was created by Packer, deleting ...")
|
||||
_, errChan := s.client.GroupsClient.Delete(resourceGroupName, cancelCh)
|
||||
|
@ -97,6 +62,61 @@ func (s *StepDeleteResourceGroup) deleteResourceGroup(state multistep.StateBag,
|
|||
}
|
||||
}
|
||||
|
||||
func (s *StepDeleteResourceGroup) deleteDeploymentResources(deploymentName, resourceGroupName string) error {
|
||||
maxResources := int32(maxResourcesToDelete)
|
||||
|
||||
deploymentOperations, err := s.client.DeploymentOperationsClient.List(resourceGroupName, deploymentName, &maxResources)
|
||||
if err != nil {
|
||||
s.reportIfError(err, resourceGroupName)
|
||||
return err
|
||||
}
|
||||
|
||||
for _, deploymentOperation := range *deploymentOperations.Value {
|
||||
// Sometimes an empty operation is added to the list by Azure
|
||||
if deploymentOperation.Properties.TargetResource == nil {
|
||||
continue
|
||||
}
|
||||
s.say(fmt.Sprintf(" -> %s : '%s'",
|
||||
*deploymentOperation.Properties.TargetResource.ResourceType,
|
||||
*deploymentOperation.Properties.TargetResource.ResourceName))
|
||||
|
||||
var networkDeleteFunction func(string, string, <-chan struct{}) (<-chan autorest.Response, <-chan error)
|
||||
resourceName := *deploymentOperation.Properties.TargetResource.ResourceName
|
||||
|
||||
switch *deploymentOperation.Properties.TargetResource.ResourceType {
|
||||
case "Microsoft.Compute/virtualMachines":
|
||||
_, errChan := s.client.VirtualMachinesClient.Delete(resourceGroupName, resourceName, nil)
|
||||
err := <-errChan
|
||||
s.reportIfError(err, resourceName)
|
||||
case "Microsoft.KeyVault/vaults":
|
||||
_, err := s.client.VaultClientDelete.Delete(resourceGroupName, resourceName)
|
||||
s.reportIfError(err, resourceName)
|
||||
case "Microsoft.Network/networkInterfaces":
|
||||
networkDeleteFunction = s.client.InterfacesClient.Delete
|
||||
case "Microsoft.Network/virtualNetworks":
|
||||
networkDeleteFunction = s.client.VirtualNetworksClient.Delete
|
||||
case "Microsoft.Network/publicIPAddresses":
|
||||
networkDeleteFunction = s.client.PublicIPAddressesClient.Delete
|
||||
}
|
||||
if networkDeleteFunction != nil {
|
||||
_, errChan := networkDeleteFunction(resourceGroupName, resourceName, nil)
|
||||
err := <-errChan
|
||||
s.reportIfError(err, resourceName)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *StepDeleteResourceGroup) reportIfError(err error, resourceName string) {
|
||||
if err != nil {
|
||||
s.say(fmt.Sprintf("Error deleting resource. Please delete manually.\n\n"+
|
||||
"Name: %s\n"+
|
||||
"Error: %s", resourceName, err.Error()))
|
||||
s.error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *StepDeleteResourceGroup) Run(state multistep.StateBag) multistep.StepAction {
|
||||
s.say("Deleting resource group ...")
|
||||
|
||||
|
|
|
@ -22,15 +22,17 @@ type StepDeployTemplate struct {
|
|||
error func(e error)
|
||||
config *Config
|
||||
factory templateFactoryFunc
|
||||
name string
|
||||
}
|
||||
|
||||
func NewStepDeployTemplate(client *AzureClient, ui packer.Ui, config *Config, factory templateFactoryFunc) *StepDeployTemplate {
|
||||
func NewStepDeployTemplate(client *AzureClient, ui packer.Ui, config *Config, deploymentName string, factory templateFactoryFunc) *StepDeployTemplate {
|
||||
var step = &StepDeployTemplate{
|
||||
client: client,
|
||||
say: func(message string) { ui.Say(message) },
|
||||
error: func(e error) { ui.Error(e.Error()) },
|
||||
config: config,
|
||||
factory: factory,
|
||||
name: deploymentName,
|
||||
}
|
||||
|
||||
step.deploy = step.deployTemplate
|
||||
|
@ -59,15 +61,14 @@ func (s *StepDeployTemplate) Run(state multistep.StateBag) multistep.StepAction
|
|||
s.say("Deploying deployment template ...")
|
||||
|
||||
var resourceGroupName = state.Get(constants.ArmResourceGroupName).(string)
|
||||
var deploymentName = state.Get(constants.ArmDeploymentName).(string)
|
||||
|
||||
s.say(fmt.Sprintf(" -> ResourceGroupName : '%s'", resourceGroupName))
|
||||
s.say(fmt.Sprintf(" -> DeploymentName : '%s'", deploymentName))
|
||||
s.say(fmt.Sprintf(" -> DeploymentName : '%s'", s.name))
|
||||
|
||||
result := common.StartInterruptibleTask(
|
||||
func() bool { return common.IsStateCancelled(state) },
|
||||
func(cancelCh <-chan struct{}) error {
|
||||
return s.deploy(resourceGroupName, deploymentName, cancelCh)
|
||||
return s.deploy(resourceGroupName, s.name, cancelCh)
|
||||
},
|
||||
)
|
||||
|
||||
|
@ -104,6 +105,9 @@ func (s *StepDeployTemplate) deleteOperationResource(resourceType string, resour
|
|||
return err
|
||||
|
||||
}
|
||||
case "Microsoft.KeyVault/vaults":
|
||||
_, err := s.client.VaultClientDelete.Delete(resourceGroupName, resourceName)
|
||||
return err
|
||||
case "Microsoft.Network/networkInterfaces":
|
||||
networkDeleteFunction = s.client.InterfacesClient.Delete
|
||||
case "Microsoft.Network/virtualNetworks":
|
||||
|
@ -142,7 +146,6 @@ func (s *StepDeployTemplate) deleteImage(imageType string, imageName string, res
|
|||
blob := s.client.BlobStorageClient.GetContainerReference(storageAccountName).GetBlobReference(blobName)
|
||||
err = blob.Delete(nil)
|
||||
return err
|
||||
|
||||
}
|
||||
|
||||
func (s *StepDeployTemplate) Cleanup(state multistep.StateBag) {
|
||||
|
@ -158,7 +161,7 @@ func (s *StepDeployTemplate) Cleanup(state multistep.StateBag) {
|
|||
|
||||
var resourceGroupName = state.Get(constants.ArmResourceGroupName).(string)
|
||||
var computeName = state.Get(constants.ArmComputeName).(string)
|
||||
var deploymentName = state.Get(constants.ArmDeploymentName).(string)
|
||||
var deploymentName = s.name
|
||||
imageType, imageName, err := s.disk(resourceGroupName, computeName)
|
||||
if err != nil {
|
||||
ui.Error("Could not retrieve OS Image details")
|
||||
|
|
|
@ -61,6 +61,7 @@ func TestStepDeployTemplateShouldTakeStepArgumentsFromStateBag(t *testing.T) {
|
|||
},
|
||||
say: func(message string) {},
|
||||
error: func(e error) {},
|
||||
name: "--deployment-name--",
|
||||
}
|
||||
|
||||
stateBag := createTestStateBagStepValidateTemplate()
|
||||
|
@ -70,10 +71,9 @@ func TestStepDeployTemplateShouldTakeStepArgumentsFromStateBag(t *testing.T) {
|
|||
t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result)
|
||||
}
|
||||
|
||||
var expectedDeploymentName = stateBag.Get(constants.ArmDeploymentName).(string)
|
||||
var expectedResourceGroupName = stateBag.Get(constants.ArmResourceGroupName).(string)
|
||||
|
||||
if actualDeploymentName != expectedDeploymentName {
|
||||
if actualDeploymentName != "--deployment-name--" {
|
||||
t.Fatal("Expected StepValidateTemplate to source 'constants.ArmDeploymentName' from the state bag, but it did not.")
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ const (
|
|||
ArmComputeName string = "arm.ComputeName"
|
||||
ArmImageParameters string = "arm.ImageParameters"
|
||||
ArmCertificateUrl string = "arm.CertificateUrl"
|
||||
ArmKeyVaultDeploymentName string = "arm.KeyVaultDeploymentName"
|
||||
ArmDeploymentName string = "arm.DeploymentName"
|
||||
ArmNicName string = "arm.NicName"
|
||||
ArmKeyVaultName string = "arm.KeyVaultName"
|
||||
|
|
|
@ -9,15 +9,18 @@ import (
|
|||
"net/url"
|
||||
|
||||
"github.com/Azure/go-autorest/autorest"
|
||||
"github.com/Azure/go-autorest/autorest/azure"
|
||||
)
|
||||
|
||||
const (
|
||||
AzureVaultApiVersion = "2015-06-01"
|
||||
AzureVaultApiVersion = "2016-10-01"
|
||||
)
|
||||
|
||||
type VaultClient struct {
|
||||
autorest.Client
|
||||
keyVaultEndpoint url.URL
|
||||
SubscriptionID string
|
||||
baseURI string
|
||||
}
|
||||
|
||||
func NewVaultClient(keyVaultEndpoint url.URL) VaultClient {
|
||||
|
@ -26,6 +29,13 @@ func NewVaultClient(keyVaultEndpoint url.URL) VaultClient {
|
|||
}
|
||||
}
|
||||
|
||||
func NewVaultClientWithBaseURI(baseURI, subscriptionID string) VaultClient {
|
||||
return VaultClient{
|
||||
baseURI: baseURI,
|
||||
SubscriptionID: subscriptionID,
|
||||
}
|
||||
}
|
||||
|
||||
type Secret struct {
|
||||
ID *string `json:"id,omitempty"`
|
||||
Value string `json:"value"`
|
||||
|
@ -76,6 +86,72 @@ func (client *VaultClient) GetSecret(vaultName, secretName string) (*Secret, err
|
|||
return &secret, 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
|
||||
// to delete
|
||||
func (client *VaultClient) Delete(resourceGroupName string, vaultName string) (result autorest.Response, err error) {
|
||||
req, err := client.DeletePreparer(resourceGroupName, vaultName)
|
||||
if err != nil {
|
||||
err = autorest.NewErrorWithError(err, "keyvault.VaultsClient", "Delete", nil, "Failure preparing request")
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := client.DeleteSender(req)
|
||||
if err != nil {
|
||||
result.Response = resp
|
||||
err = autorest.NewErrorWithError(err, "keyvault.VaultsClient", "Delete", resp, "Failure sending request")
|
||||
return
|
||||
}
|
||||
|
||||
result, err = client.DeleteResponder(resp)
|
||||
if err != nil {
|
||||
err = autorest.NewErrorWithError(err, "keyvault.VaultsClient", "Delete", resp, "Failure responding to request")
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// DeletePreparer prepares the Delete request.
|
||||
func (client *VaultClient) DeletePreparer(resourceGroupName string, vaultName string) (*http.Request, error) {
|
||||
pathParameters := map[string]interface{}{
|
||||
"resourceGroupName": autorest.Encode("path", resourceGroupName),
|
||||
"SubscriptionID": autorest.Encode("path", client.SubscriptionID),
|
||||
"vaultName": autorest.Encode("path", vaultName),
|
||||
}
|
||||
|
||||
queryParameters := map[string]interface{}{
|
||||
"api-version": AzureVaultApiVersion,
|
||||
}
|
||||
|
||||
preparer := autorest.CreatePreparer(
|
||||
autorest.AsDelete(),
|
||||
autorest.WithBaseURL(client.baseURI),
|
||||
autorest.WithPathParameters("/subscriptions/{SubscriptionID}/resourceGroups/{resourceGroupName}/providers/Microsoft.KeyVault/vaults/{vaultName}", pathParameters),
|
||||
autorest.WithQueryParameters(queryParameters))
|
||||
return preparer.Prepare(&http.Request{})
|
||||
}
|
||||
|
||||
// DeleteSender sends the Delete request. The method will close the
|
||||
// http.Response Body if it receives an error.
|
||||
func (client *VaultClient) DeleteSender(req *http.Request) (*http.Response, error) {
|
||||
return autorest.SendWithSender(client,
|
||||
req,
|
||||
azure.DoPollForAsynchronous(client.PollingDelay))
|
||||
}
|
||||
|
||||
// DeleteResponder handles the response to the Delete request. The method always
|
||||
// closes the http.Response Body.
|
||||
func (client *VaultClient) DeleteResponder(resp *http.Response) (result autorest.Response, err error) {
|
||||
err = autorest.Respond(
|
||||
resp,
|
||||
client.ByInspecting(),
|
||||
azure.WithErrorUnlessStatusCode(http.StatusOK),
|
||||
autorest.ByClosing())
|
||||
result.Response = resp
|
||||
return
|
||||
}
|
||||
|
||||
func (client *VaultClient) getVaultUrl(vaultName string) string {
|
||||
return fmt.Sprintf("%s://%s.%s/", client.keyVaultEndpoint.Scheme, vaultName, client.keyVaultEndpoint.Host)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue