title: Execute a custom script on Azure VM Deletion
body: <p>Is it possible to execute a separate script when uninstalling the Azure VM Custom Script Extension <em>OR</em> VM Deletion than what ran during install?</p>
  <p>Our use case:</p>
  <ul>
  <li>On Extension <strong>Install:</strong> Install software and add licensing (e.g. using <code>install.ps1</code>)</li>
  <li>On Extension <strong>Uninstall:</strong> Remove licensing (e.g. using <code>uninstall.ps1</code>)</li>
  </ul>
  <p>We cannot find a clean way to automatically execute clean up script on extension uninstall / VM delete.</p>
  <hr />
  <p>Below is a Terraform example used to create VM and the hilighted problem area that we cannot implement:</p>
  <pre><code>provider &quot;azurerm&quot; {
    skip_provider_registration = true
    features {
      virtual_machine {
        delete_os_disk_on_deletion = true
        graceful_shutdown          = true
      }
    }
  }
  
  resource &quot;azurerm_resource_group&quot; &quot;test&quot; {
    name     = &quot;test-resources&quot;
    location = &quot;CanadaCentral&quot;
  }
  
  resource &quot;azurerm_virtual_network&quot; &quot;test&quot; {
    name                = &quot;test-network&quot;
    address_space       = [&quot;10.9.0.0/16&quot;]
    location            = azurerm_resource_group.test.location
    resource_group_name = azurerm_resource_group.test.name
  }
  
  resource &quot;azurerm_subnet&quot; &quot;test&quot; {
    name                 = &quot;internal&quot;
    resource_group_name  = azurerm_resource_group.test.name
    virtual_network_name = azurerm_virtual_network.test.name
    address_prefixes     = [&quot;10.9.2.0/24&quot;]
  }
  
  resource &quot;azurerm_network_security_group&quot; &quot;test&quot; {
    name                = &quot;test-nsg&quot;
    location            = azurerm_resource_group.test.location
    resource_group_name = azurerm_resource_group.test.name
  }
  
  resource &quot;azurerm_network_security_rule&quot; &quot;AllowPSRemoting&quot; {
    access                      = &quot;Allow&quot;
    destination_address_prefix  = &quot;*&quot;
    destination_port_range      = &quot;5986&quot;
    direction                   = &quot;Inbound&quot;
    name                        = &quot;AllowPSRemoting&quot;
    network_security_group_name = azurerm_network_security_group.test.name
    priority                    = 301
    protocol                    = &quot;Tcp&quot;
    resource_group_name         = azurerm_resource_group.test.name
    source_address_prefix       = &quot;*&quot;
    source_port_range           = &quot;*&quot;
  }
  
  resource &quot;azurerm_subnet_network_security_group_association&quot; &quot;test&quot; {
    subnet_id                 = azurerm_subnet.test.id
    network_security_group_id = azurerm_network_security_group.test.id
  }
  
  # resource &quot;azurerm_public_ip&quot; &quot;test&quot; {
  #   name                = &quot;acceptanceTestPublicIp1&quot;
  #   resource_group_name = azurerm_resource_group.test.name
  #   location            = azurerm_resource_group.test.location
  #   allocation_method   = &quot;Dynamic&quot;
  # }
  
  resource &quot;azurerm_network_interface&quot; &quot;test&quot; {
    name                = &quot;test-nic&quot;
    location            = azurerm_resource_group.test.location
    resource_group_name = azurerm_resource_group.test.name
  
    ip_configuration {
      name                          = &quot;internal&quot;
      subnet_id                     = azurerm_subnet.test.id
      private_ip_address_allocation = &quot;Dynamic&quot;
      # public_ip_address_id          = azurerm_public_ip.test.id
    }
  }
  
  locals {
    first_logon_script = &lt;&lt;-SCRIPT
      netsh advfirewall firewall add rule name=WinRM_HTTP_5985 protocol=TCP dir=in localport=5985 action=allow
    SCRIPT
  
    install_script = &lt;&lt;-SCRIPT
      # Invoke-WebRequest -Method &quot;POST&quot; -Uri &quot;https://&lt;vendor_api&gt;/register&quot; -Body &quot;...&quot;
  
      $logFile = &quot;C:\install.log&quot;
  
      If (-not (Test-Path -path $logFile -ErrorAction &quot;SilentlyContinue&quot;)) {
          New-Item -Path $logFile -ItemType &quot;File&quot;
      }
  
      Add-Content -Path $logFile -Value &quot;$(Get-Date) Ran install.ps1&quot;
    SCRIPT
  
    uninstall_script = &lt;&lt;-SCRIPT
      # Invoke-WebRequest -Method &quot;POST&quot; -Uri &quot;https://&lt;vendor_api&gt;/unregister&quot; -Body &quot;...&quot;
  
      $logFile = &quot;C:\install.log&quot;
  
      If (-not (Test-Path -path $logFile -ErrorAction &quot;SilentlyContinue&quot;)) {
          New-Item -Path $logFile -ItemType &quot;File&quot;
      }
  
      Add-Content -Path $logFile -Value &quot;$(Get-Date) Ran uninstall.ps1&quot;
    SCRIPT
  }
  
  resource &quot;local_file&quot; &quot;install_script&quot; {
    filename = &quot;${path.module}/install.ps1&quot;
    content = local.install_script
  }
  
  resource &quot;local_file&quot; &quot;uninstall_script&quot; {
    filename = &quot;${path.module}/uninstall.ps1&quot;
    content = local.uninstall_script
  }
  
  locals {
    username = &quot;adminuser&quot;
    password = &quot;P@$$w0rd1234!&quot;
  }
  
  resource &quot;azurerm_windows_virtual_machine&quot; &quot;test&quot; {
    name                = &quot;test-machine&quot;
    resource_group_name = azurerm_resource_group.test.name
    location            = azurerm_resource_group.test.location
    size                = &quot;Standard_F2&quot;
    admin_username      = local.username
    admin_password      = local.password
    priority            = &quot;Regular&quot;
    provision_vm_agent  = true
  
    network_interface_ids = [
      azurerm_network_interface.test.id,
    ]
  
    os_disk {
      caching              = &quot;ReadWrite&quot;
      storage_account_type = &quot;Standard_LRS&quot;
    }
  
    source_image_reference {
      publisher = &quot;MicrosoftWindowsServer&quot;
      offer     = &quot;WindowsServer&quot;
      sku       = &quot;2016-Datacenter&quot;
      version   = &quot;latest&quot;
    }
  
    ############################################################
    # Need help with this region below
    # 1. Create `install.ps1` file locally on remote Windows VM with predefined content
    # 2. Create `uninstall.ps1` file locally on remote Windows VM with predefined content
    # 3. Execute `install.ps1` file on VM creation
    # 4. Execute `uninstall.ps1` file on VM destroy
  
    winrm_listener {
      protocol = &quot;Http&quot;
    }
  
    additional_unattend_content {
      setting = &quot;AutoLogon&quot;
      content = &quot;&lt;AutoLogon&gt;&lt;Password&gt;&lt;Value&gt;${local.username}&lt;/Value&gt;&lt;/Password&gt;&lt;Enabled&gt;true&lt;/Enabled&gt;&lt;LogonCount&gt;1&lt;/LogonCount&gt;&lt;Username&gt;${local.password}&lt;/Username&gt;&lt;/AutoLogon&gt;&quot;
    }
  
    additional_unattend_content {
      setting = &quot;FirstLogonCommands&quot;
      content = &quot;&lt;FirstLogonCommands&gt;&lt;SynchronousCommand&gt;&lt;CommandLine&gt;${local.first_logon_script}&lt;/CommandLine&gt;&lt;Order&gt;1&lt;/Order&gt;&lt;/SynchronousCommand&gt;&lt;/FirstLogonCommands&gt;&quot;
    }
  
    # Connection used by `file` and `remote-exec` provisioners
    connection {
      host     = self.private_ip_address
      type     = &quot;winrm&quot;
      user     = self.admin_username
      password = self.admin_password
      timeout  = &quot;5m&quot;
      https    = false
      port     = &quot;5985&quot;
      insecure = true
    }
  
    # Copy install file during creation
    provisioner &quot;file&quot; {
      source      = local_file.install_script.filename
      destination = &quot;C:/install.txt&quot;
      when        = create
    }
  
    # Run uninstall file during creation
    provisioner &quot;remote-exec&quot; {
      when   = create
      script = &quot;C:/install.ps1&quot;
    }
  
    # Copy uninstall file during creation
    provisioner &quot;file&quot; {
      source      = local_file.uninstall_script.filename
      destination = &quot;C:/uninstall.txt&quot;
      when        = create
    }
  
    # Run uninstall file during destruction
    provisioner &quot;remote-exec&quot; {
      when   = destroy
      script = &quot;C:/uninstall.ps1&quot;
    }
  
    # Need help with above section
    ############################################################
  }
  
  </code></pre>
  <p>Currently seeing this error:</p>
  <pre class="lang-sh prettyprint-override"><code>timeout - last error: http response error: 401 - invalid content type
  </code></pre>
  <p>This is similar to question: <a href="https://stackoverflow.com/questions/62224835/terraform-file-provisioner-to-upload-to-azure-vm-using-winrm-error-i-o-timeout">Terraform File Provisioner to Upload to Azure VM Using WinRM: Error: i/o timeout</a></p>
  <p>However, unlike that question, we don't need HTTPS yet. Even HTTP is showing the same error.</p>
  <p>Terraform version details:</p>
  <pre class="lang-sh prettyprint-override"><code>Terraform v1.0.9
  on windows_amd64
  + provider registry.terraform.io/hashicorp/azurerm v2.82.0
  </code></pre>