From a1c6786d0e227db6db7277cc14ea72180acb1707 Mon Sep 17 00:00:00 2001 From: Russell Seymour Date: Tue, 14 Feb 2017 18:35:28 +0000 Subject: [PATCH 1/2] Added support to selectively delete items in the RG Signed-off-by: Russell Seymour --- generator_files/templates/arm.json.erb | 2 +- generator_files/templates/arm.tidy.json.erb | 32 ++++++++ lib/wombat/cli.rb | 9 +++ lib/wombat/common.rb | 81 ++++++++++++++++++--- lib/wombat/delete.rb | 54 ++++++++++++-- lib/wombat/deploy.rb | 52 ------------- wombat-cli.gemspec | 4 +- 7 files changed, 164 insertions(+), 70 deletions(-) create mode 100644 generator_files/templates/arm.tidy.json.erb diff --git a/generator_files/templates/arm.json.erb b/generator_files/templates/arm.json.erb index cc68e2e..181df63 100644 --- a/generator_files/templates/arm.json.erb +++ b/generator_files/templates/arm.json.erb @@ -45,7 +45,7 @@ "sa": { "name": "[parameters('storageAccountName')]", - "container": "vhds", + "container": "[concat('vhds-', variables('unique'))]", "type": "Standard_LRS" }, diff --git a/generator_files/templates/arm.tidy.json.erb b/generator_files/templates/arm.tidy.json.erb new file mode 100644 index 0000000..54f540a --- /dev/null +++ b/generator_files/templates/arm.tidy.json.erb @@ -0,0 +1,32 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.1", + "parameters": { + "storageAccountName": { + "type": "string", + "metadata": { + "description": "Name of the storage account that should be used to store the machine disks" + }, + "defaultValue": "<%= @storage_account %>" + } + }, + "variables": { + "sa": { + "name": "[parameters('storageAccountName')]", + "type": "Standard_LRS" + }, + "location": "[resourceGroup().location]" + }, + "resources": [ + { + "type": "Microsoft.Storage/storageAccounts", + "name": "[variables('sa').name]", + "apiVersion": "2015-06-15", + "location": "[variables('location')]", + "properties": { + "accountType": "[variables('sa').type]" + } + } + ], + "outputs": {} +} \ No newline at end of file diff --git a/lib/wombat/cli.rb b/lib/wombat/cli.rb index 87f26d7..e45decc 100644 --- a/lib/wombat/cli.rb +++ b/lib/wombat/cli.rb @@ -89,6 +89,15 @@ def self.parse(args) opts.on("-c CLOUD", "--cloud CLOUD", "Select cloud") do |opt| options.cloud = opt end + + opts.on("--all", "Remove entire Resource Group which includes images (Azure Only)") do |opt| + options.remove_all = opt + end + + opts.on("--async", "Delete resources asynchronously when not removing all, e.g. do not block command line. (Azure Only)") do |opt| + options.azure_async = opt + end + }, argv: stack_argv_proc }, diff --git a/lib/wombat/common.rb b/lib/wombat/common.rb index 1c6408d..43ec019 100644 --- a/lib/wombat/common.rb +++ b/lib/wombat/common.rb @@ -206,11 +206,17 @@ def update_template(cloud) warn('No lock - skipping template creation') else + @demo = lock['name'] + @version = lock['version'] + @ttl = lock['ttl'] + # Determine the region/location/zone for the specific cloud case cloud when 'aws' region = lock['aws']['region'] - template_file = "cfn.json.erb" + template_files = { + "cfn.json.erb": "#{conf['stack_dir']}/#{@demo}.json" + } @chef_server_ami = lock['amis'][region]['chef-server'] @automate_ami = lock['amis'][region]['automate'] @compliance_ami = lock['amis'][region]['compliance'] @@ -219,7 +225,10 @@ def update_template(cloud) when 'azure' region = lock['azure']['location'] @storage_account = lock['azure']['storage_account'] - template_file = "arm.json.erb" + template_files = { + "arm.json.erb": "#{conf['stack_dir']}/#{@demo}.json", + "arm.tidy.json.erb": "#{conf['stack_dir']}/#{@demo}.tidy.json" + } @chef_server_uri = lock['amis'][region]['chef-server'] @automate_uri = lock['amis'][region]['automate'] @compliance_uri = lock['amis'][region]['compliance'] @@ -249,14 +258,13 @@ def update_template(cloud) end end - @demo = lock['name'] - @version = lock['version'] - @ttl = lock['ttl'] - - rendered_cfn = ERB.new(File.read("#{conf['template_dir']}/#{template_file}"), nil, '-').result(binding) - Dir.mkdir(conf['stack_dir'], 0755) unless File.exist?(conf['stack_dir']) - File.open("#{conf['stack_dir']}/#{@demo}.json", 'w') { |file| file.puts rendered_cfn } - banner("Generated: #{conf['stack_dir']}/#{@demo}.json") + # Iterate around each of the template files that have been defined and render it + template_files.each do |template_file, destination| + rendered_cfn = ERB.new(File.read("#{conf['template_dir']}/#{template_file}"), nil, '-').result(binding) + Dir.mkdir(conf['stack_dir'], 0755) unless File.exist?(conf['stack_dir']) + File.open("#{destination}", 'w') { |file| file.puts rendered_cfn } + banner("Generated: #{destination}") + end end end @@ -268,5 +276,58 @@ def is_valid_json?(file) false end end + + # Track the progress of the deployment in Azure + # + # ===== Attributes + # + # * +rg_name+ - Name of the resource group being deployed to + # * +deployment_name+ - Name of the deployment that is currently being processed + def follow_azure_deployment(rg_name, deployment_name) + + end_provisioning_states = 'Canceled,Failed,Deleted,Succeeded' + end_provisioning_state_reached = false + + until end_provisioning_state_reached + list_outstanding_deployment_operations(rg_name, deployment_name) + sleep 10 + deployment_provisioning_state = deployment_state(rg_name, deployment_name) + end_provisioning_state_reached = end_provisioning_states.split(',').include?(deployment_provisioning_state) + end + info format("Resource Template deployment reached end state of %s", deployment_provisioning_state) + end + + # Get a list of the outstanding deployment operations + # + # ===== Attributes + # + # * +rg_name+ - Name of the resource group being deployed to + # * +deployment_name+ - Name of the deployment that is currently being processed + def list_outstanding_deployment_operations(rg_name, deployment_name) + end_operation_states = 'Failed,Succeeded' + deployment_operations = resource_management_client.deployment_operations.list(rg_name, deployment_name) + deployment_operations.each do |val| + resource_provisioning_state = val.properties.provisioning_state + unless val.properties.target_resource.nil? + resource_name = val.properties.target_resource.resource_name + resource_type = val.properties.target_resource.resource_type + end + end_operation_state_reached = end_operation_states.split(',').include?(resource_provisioning_state) + unless end_operation_state_reached + info format("resource %s '%s' provisioning status is %s", resource_type, resource_name, resource_provisioning_state) + end + end + end + + # Get the state of the specified deployment + # + # ===== Attributes + # + # * +rg_name+ - Name of the resource group being deployed to + # * +deployment_name+ - Name of the deployment that is currently being processed + def deployment_state(rg_name, deployment_name) + deployments = resource_management_client.deployments.get(rg_name, deployment_name) + deployments.properties.provisioning_state + end end end \ No newline at end of file diff --git a/lib/wombat/delete.rb b/lib/wombat/delete.rb index f43a272..c36d2ec 100644 --- a/lib/wombat/delete.rb +++ b/lib/wombat/delete.rb @@ -8,10 +8,13 @@ class DeleteRunner include Wombat::Common attr_reader :stack, :cloud + attr_accessor :resource_management_client def initialize(opts) @stack = opts.stack @cloud = opts.cloud.nil? ? "aws" : opts.cloud + @remove_all = opts.remove_all.nil? ? false : opts.remove_all + @azure_async = opts.azure_async.nil? ? false : opts.azure_async end def start @@ -44,14 +47,55 @@ def cfn_delete_stack(stack) azure_conn = MsRest::TokenCredentials.new(token_provider) # Create a resource client so that the resource group can be deleted - resource_management_client = Azure::ARM::Resources::ResourceManagementClient.new(azure_conn) - resource_management_client.subscription_id = subscription_id + @resource_management_client = Azure::ARM::Resources::ResourceManagementClient.new(azure_conn) + @resource_management_client.subscription_id = subscription_id - banner(format("Deleting resource group: %s", stack)) + # Only delete the entire resource group if it has been explicitly set + if (@remove_all) + banner(format("Deleting resource group: %s", stack)) - resource_management_client.resource_groups.begin_delete(stack) + resource_management_client.resource_groups.begin_delete(stack) - info "Destroy operation accepted and will continue in the background." + info "Destroy operation accepted and will continue in the background." + else + + banner(format("Tidying resource group: %s", stack)) + + # Create new deployment using the tidy template so that the storage account is left + # behind but all the other resources are removed + template_file = File.read("#{conf['stack_dir']}/#{stack}.tidy.json") + + # determine the name of the deployment + deployment_name = format('deploy-tidy-%s', Time.now().to_i) + + # Create the deployment definition + deployment = Azure::ARM::Resources::Models::Deployment.new + deployment.properties = Azure::ARM::Resources::Models::DeploymentProperties.new + deployment.properties.mode = Azure::ARM::Resources::Models::DeploymentMode::Complete + deployment.properties.template = JSON.parse(template_file) + + # Perform the deployment to the named resource group + begin + resource_management_client.deployments.begin_create_or_update_async(stack, deployment_name, deployment).value! + rescue MsRestAzure::AzureOperationError => operation_error + rest_error = operation_error.body['error'] + deployment_active = rest_error['code'] == 'DeploymentActive' + if deployment_active + info format("Deployment for resource group '%s' is ongoing", stack) + else + warn rest_error + raise operation_error + end + end + + # Monitor the deployment + if @azure_async + info "Deployment operation accepted. Use the Azure Portal to check progress" + else + info "Removing Automate resources" + follow_azure_deployment(stack, deployment_name) + end + end end end end diff --git a/lib/wombat/deploy.rb b/lib/wombat/deploy.rb index 41e67c8..f85ebcd 100644 --- a/lib/wombat/deploy.rb +++ b/lib/wombat/deploy.rb @@ -102,59 +102,7 @@ def create_stack(stack) end end - # Track the progress of the deployment in Azure - # - # ===== Attributes - # - # * +rg_name+ - Name of the resource group being deployed to - # * +deployment_name+ - Name of the deployment that is currently being processed - def follow_azure_deployment(rg_name, deployment_name) - - end_provisioning_states = 'Canceled,Failed,Deleted,Succeeded' - end_provisioning_state_reached = false - - until end_provisioning_state_reached - list_outstanding_deployment_operations(rg_name, deployment_name) - info "" - sleep 10 - deployment_provisioning_state = deployment_state(rg_name, deployment_name) - end_provisioning_state_reached = end_provisioning_states.split(',').include?(deployment_provisioning_state) - end - info format("Resource Template deployment reached end state of %s", deployment_provisioning_state) - end - - # Get a list of the outstanding deployment operations - # - # ===== Attributes - # - # * +rg_name+ - Name of the resource group being deployed to - # * +deployment_name+ - Name of the deployment that is currently being processed - def list_outstanding_deployment_operations(rg_name, deployment_name) - end_operation_states = 'Failed,Succeeded' - deployment_operations = resource_management_client.deployment_operations.list(rg_name, deployment_name) - deployment_operations.each do |val| - resource_provisioning_state = val.properties.provisioning_state - unless val.properties.target_resource.nil? - resource_name = val.properties.target_resource.resource_name - resource_type = val.properties.target_resource.resource_type - end - end_operation_state_reached = end_operation_states.split(',').include?(resource_provisioning_state) - unless end_operation_state_reached - info format("resource %s '%s' provisioning status is %s", resource_type, resource_name, resource_provisioning_state) - end - end - end - # Get the state of the specified deployment - # - # ===== Attributes - # - # * +rg_name+ - Name of the resource group being deployed to - # * +deployment_name+ - Name of the deployment that is currently being processed - def deployment_state(rg_name, deployment_name) - deployments = resource_management_client.deployments.get(rg_name, deployment_name) - deployments.properties.provisioning_state - end end end diff --git a/wombat-cli.gemspec b/wombat-cli.gemspec index 84a2390..653a42e 100644 --- a/wombat-cli.gemspec +++ b/wombat-cli.gemspec @@ -28,6 +28,6 @@ Gem::Specification.new do |gem| gem.add_dependency 'net-ssh', '~> 3.2' gem.add_dependency 'parallel', '~> 1.9' gem.add_dependency 'aws-sdk', '~> 2.5' - gem.add_dependency 'azure_mgmt_resources', '~> 0.8' - gem.add_dependency 'azure_mgmt_storage', '~> 0.8' + gem.add_dependency 'azure_mgmt_resources', '~> 0.9' + gem.add_dependency 'azure_mgmt_storage', '~> 0.9' end From b3d9562ffab762a7de1963048aebecd46a0d3062 Mon Sep 17 00:00:00 2001 From: Bakh Inamov Date: Wed, 15 Feb 2017 08:52:11 -0800 Subject: [PATCH 2/2] needs /oobe after all --- generator_files/packer/workstation.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/generator_files/packer/workstation.json b/generator_files/packer/workstation.json index 0b9946b..ba24681 100644 --- a/generator_files/packer/workstation.json +++ b/generator_files/packer/workstation.json @@ -130,7 +130,7 @@ ], "type": "windows-shell", "inline": [ - "C:\\Windows\\System32\\sysprep\\sysprep.exe /quiet /generalize /shutdown" + "C:\\Windows\\System32\\sysprep\\sysprep.exe /quiet /generalize /oobe /shutdown" ] } ]