diff --git a/azurerm/config.go b/azurerm/config.go index 9dc7fc953693..4811f3c243b6 100644 --- a/azurerm/config.go +++ b/azurerm/config.go @@ -152,6 +152,7 @@ type ArmClient struct { ifaceClient network.InterfacesClient loadBalancerClient network.LoadBalancersClient localNetConnClient network.LocalNetworkGatewaysClient + packetCapturesClient network.PacketCapturesClient publicIPClient network.PublicIPAddressesClient routesClient network.RoutesClient routeTablesClient network.RouteTablesClient @@ -737,6 +738,10 @@ func (c *ArmClient) registerNetworkingClients(endpoint, subscriptionId string, a c.configureClient(&networksClient.Client, auth) c.vnetClient = networksClient + packetCapturesClient := network.NewPacketCapturesClientWithBaseURI(endpoint, subscriptionId) + c.configureClient(&packetCapturesClient.Client, auth) + c.packetCapturesClient = packetCapturesClient + peeringsClient := network.NewVirtualNetworkPeeringsClientWithBaseURI(endpoint, subscriptionId) c.configureClient(&peeringsClient.Client, auth) c.vnetPeeringsClient = peeringsClient diff --git a/azurerm/import_arm_packet_capture_test.go b/azurerm/import_arm_packet_capture_test.go new file mode 100644 index 000000000000..f3deee3624d6 --- /dev/null +++ b/azurerm/import_arm_packet_capture_test.go @@ -0,0 +1,33 @@ +package azurerm + +import ( + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" +) + +func testAccAzureRMPacketCapture_importBasic(t *testing.T) { + rInt := acctest.RandInt() + rString := acctest.RandString(6) + location := testLocation() + + resourceName := "azurerm_packet_capture.test" + config := testAzureRMPacketCapture_storageAccountAndLocalDiskConfig(rInt, rString, location) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMPacketCaptureDestroy, + Steps: []resource.TestStep{ + { + Config: config, + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} diff --git a/azurerm/provider.go b/azurerm/provider.go index fd55f5f944d6..45e1b28a1dc4 100644 --- a/azurerm/provider.go +++ b/azurerm/provider.go @@ -169,6 +169,7 @@ func Provider() terraform.ResourceProvider { "azurerm_network_security_group": resourceArmNetworkSecurityGroup(), "azurerm_network_security_rule": resourceArmNetworkSecurityRule(), "azurerm_network_watcher": resourceArmNetworkWatcher(), + "azurerm_packet_capture": resourceArmPacketCapture(), "azurerm_policy_definition": resourceArmPolicyDefinition(), "azurerm_postgresql_configuration": resourceArmPostgreSQLConfiguration(), "azurerm_postgresql_database": resourceArmPostgreSQLDatabase(), diff --git a/azurerm/resource_arm_network_watcher_test.go b/azurerm/resource_arm_network_watcher_test.go index 114e4f6fcddd..b9f97881b274 100644 --- a/azurerm/resource_arm_network_watcher_test.go +++ b/azurerm/resource_arm_network_watcher_test.go @@ -24,6 +24,13 @@ func TestAccAzureRMNetworkWatcher(t *testing.T) { "importBasic": testAccAzureRMNetworkWatcher_importBasic, "importComplete": testAccAzureRMNetworkWatcher_importComplete, }, + "PacketCapture": { + "import": testAccAzureRMPacketCapture_importBasic, + "localDisk": testAccAzureRMPacketCapture_localDisk, + "storageAccount": testAccAzureRMPacketCapture_storageAccount, + "storageAccountAndLocalDisk": testAccAzureRMPacketCapture_storageAccountAndLocalDisk, + "withFilters": testAccAzureRMPacketCapture_withFilters, + }, } for group, m := range testCases { diff --git a/azurerm/resource_arm_packet_capture.go b/azurerm/resource_arm_packet_capture.go new file mode 100644 index 000000000000..27ff7708d919 --- /dev/null +++ b/azurerm/resource_arm_packet_capture.go @@ -0,0 +1,368 @@ +package azurerm + +import ( + "fmt" + + "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2017-09-01/network" + "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/helper/validation" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/response" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func resourceArmPacketCapture() *schema.Resource { + return &schema.Resource{ + Create: resourceArmPacketCaptureCreate, + Read: resourceArmPacketCaptureRead, + Delete: resourceArmPacketCaptureDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "resource_group_name": resourceGroupNameSchema(), + + "network_watcher_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "target_resource_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "maximum_bytes_per_packet": { + Type: schema.TypeInt, + Optional: true, + ForceNew: true, + Default: 0, + }, + + "maximum_bytes_per_session": { + Type: schema.TypeInt, + Optional: true, + ForceNew: true, + Default: 1073741824, + }, + + "maximum_capture_duration": { + Type: schema.TypeInt, + Optional: true, + ForceNew: true, + Default: 18000, + ValidateFunc: validation.IntBetween(1, 18000), + }, + + "storage_location": { + Type: schema.TypeList, + Required: true, + ForceNew: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "file_path": { + Type: schema.TypeString, + Optional: true, + }, + "storage_account_id": { + Type: schema.TypeString, + Optional: true, + }, + "storage_path": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + + "filter": { + Type: schema.TypeList, + Optional: true, + ForceNew: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "local_ip_address": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + "local_port": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + "protocol": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{ + string(network.PcProtocolAny), + string(network.PcProtocolTCP), + string(network.PcProtocolUDP), + }, false), + }, + "remote_ip_address": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + "remote_port": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + }, + }, + }, + }, + } +} + +func resourceArmPacketCaptureCreate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).packetCapturesClient + ctx := meta.(*ArmClient).StopContext + + name := d.Get("name").(string) + watcherName := d.Get("network_watcher_name").(string) + resourceGroup := d.Get("resource_group_name").(string) + + targetResourceId := d.Get("target_resource_id").(string) + bytesToCapturePerPacket := d.Get("maximum_bytes_per_packet").(int) + totalBytesPerSession := d.Get("maximum_bytes_per_session").(int) + timeLimitInSeconds := d.Get("maximum_capture_duration").(int) + + storageLocation, err := expandArmPacketCaptureStorageLocation(d) + if err != nil { + return err + } + + filters, err := expandArmPacketCaptureFilters(d) + if err != nil { + return err + } + + properties := network.PacketCapture{ + PacketCaptureParameters: &network.PacketCaptureParameters{ + Target: utils.String(targetResourceId), + StorageLocation: storageLocation, + BytesToCapturePerPacket: utils.Int32(int32(bytesToCapturePerPacket)), + TimeLimitInSeconds: utils.Int32(int32(timeLimitInSeconds)), + TotalBytesPerSession: utils.Int32(int32(totalBytesPerSession)), + Filters: filters, + }, + } + + future, err := client.Create(ctx, resourceGroup, watcherName, name, properties) + if err != nil { + return fmt.Errorf("Error creating Packet Capture %q (Watcher %q / Resource Group %q): %+v", name, watcherName, resourceGroup, err) + } + + err = future.WaitForCompletion(ctx, client.Client) + if err != nil { + return fmt.Errorf("Error waiting for creation of Packet Capture %q (Watcher %q / Resource Group %q): %+v", name, watcherName, resourceGroup, err) + } + + resp, err := client.Get(ctx, resourceGroup, watcherName, name) + if err != nil { + return fmt.Errorf("Error retrieving Packet Capture %q (Watcher %q / Resource Group %q): %+v", name, watcherName, resourceGroup, err) + } + + d.SetId(*resp.ID) + + return resourceArmPacketCaptureRead(d, meta) +} + +func resourceArmPacketCaptureRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).packetCapturesClient + ctx := meta.(*ArmClient).StopContext + + id, err := parseAzureResourceID(d.Id()) + if err != nil { + return err + } + + resourceGroup := id.ResourceGroup + watcherName := id.Path["networkWatchers"] + name := id.Path["packetCaptures"] + + resp, err := client.Get(ctx, resourceGroup, watcherName, name) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + d.SetId("") + return nil + } + + return fmt.Errorf("Error reading Packet Capture %q (Watcher %q / Resource Group %q) %+v", name, watcherName, resourceGroup, err) + } + + d.Set("name", name) + d.Set("network_watcher_name", watcherName) + d.Set("resource_group_name", resourceGroup) + + if props := resp.PacketCaptureResultProperties; props != nil { + d.Set("target_resource_id", props.Target) + d.Set("maximum_bytes_per_packet", int(*props.BytesToCapturePerPacket)) + d.Set("maximum_bytes_per_session", int(*props.TotalBytesPerSession)) + d.Set("maximum_capture_duration", int(*props.TimeLimitInSeconds)) + + location := flattenArmPacketCaptureStorageLocation(props.StorageLocation) + if err := d.Set("storage_location", location); err != nil { + return fmt.Errorf("Error flattening `storage_location`: %+v", err) + } + + filters := flattenArmPacketCaptureFilters(props.Filters) + if err := d.Set("filter", filters); err != nil { + return fmt.Errorf("Error flattening `filter`: %+v", err) + } + } + + return nil +} + +func resourceArmPacketCaptureDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).packetCapturesClient + ctx := meta.(*ArmClient).StopContext + + id, err := parseAzureResourceID(d.Id()) + if err != nil { + return err + } + + resourceGroup := id.ResourceGroup + watcherName := id.Path["networkWatchers"] + name := id.Path["packetCaptures"] + + future, err := client.Delete(ctx, resourceGroup, watcherName, name) + if err != nil { + if response.WasNotFound(future.Response()) { + return nil + } + + return fmt.Errorf("Error deleting Packet Capture %q (Watcher %q / Resource Group %q): %+v", name, watcherName, resourceGroup, err) + } + + err = future.WaitForCompletion(ctx, client.Client) + if err != nil { + if response.WasNotFound(future.Response()) { + return nil + } + + return fmt.Errorf("Error waiting for the deletion of Packet Capture %q (Watcher %q / Resource Group %q): %+v", name, watcherName, resourceGroup, err) + } + + return nil +} + +func expandArmPacketCaptureStorageLocation(d *schema.ResourceData) (*network.PacketCaptureStorageLocation, error) { + locations := d.Get("storage_location").([]interface{}) + if len(locations) == 0 { + return nil, fmt.Errorf("Error expandng `storage_location`: not found") + } + + location := locations[0].(map[string]interface{}) + + storageLocation := network.PacketCaptureStorageLocation{} + + if v := location["file_path"]; v != "" { + storageLocation.FilePath = utils.String(v.(string)) + } + if v := location["storage_account_id"]; v != "" { + storageLocation.StorageID = utils.String(v.(string)) + } + + return &storageLocation, nil +} + +func flattenArmPacketCaptureStorageLocation(input *network.PacketCaptureStorageLocation) []interface{} { + if input == nil { + return []interface{}{} + } + + output := make(map[string]interface{}, 0) + + if path := input.FilePath; path != nil { + output["file_path"] = *path + } + + if account := input.StorageID; account != nil { + output["storage_account_id"] = *account + } + + if path := input.StoragePath; path != nil { + output["storage_path"] = *path + } + + return []interface{}{output} +} + +func expandArmPacketCaptureFilters(d *schema.ResourceData) (*[]network.PacketCaptureFilter, error) { + inputFilters := d.Get("filter").([]interface{}) + if len(inputFilters) == 0 { + return nil, nil + } + + filters := make([]network.PacketCaptureFilter, 0) + + for _, v := range inputFilters { + inputFilter := v.(map[string]interface{}) + + localIPAddress := inputFilter["local_ip_address"].(string) + localPort := inputFilter["local_port"].(string) // TODO: should this be an int? + protocol := inputFilter["protocol"].(string) + remoteIPAddress := inputFilter["remote_ip_address"].(string) + remotePort := inputFilter["remote_port"].(string) + + filter := network.PacketCaptureFilter{ + LocalIPAddress: utils.String(localIPAddress), + LocalPort: utils.String(localPort), + Protocol: network.PcProtocol(protocol), + RemoteIPAddress: utils.String(remoteIPAddress), + RemotePort: utils.String(remotePort), + } + filters = append(filters, filter) + } + + return &filters, nil +} + +func flattenArmPacketCaptureFilters(input *[]network.PacketCaptureFilter) []interface{} { + filters := make([]interface{}, 0) + + if inFilter := input; inFilter != nil { + for _, v := range *inFilter { + filter := make(map[string]interface{}, 0) + + if address := v.LocalIPAddress; address != nil { + filter["local_ip_address"] = *address + } + + if port := v.LocalPort; port != nil { + filter["local_port"] = *port + } + + filter["protocol"] = string(v.Protocol) + + if address := v.RemoteIPAddress; address != nil { + filter["remote_ip_address"] = *address + } + + if port := v.RemotePort; port != nil { + filter["remote_port"] = *port + } + + filters = append(filters, filter) + } + } + + return filters +} diff --git a/azurerm/resource_arm_packet_capture_test.go b/azurerm/resource_arm_packet_capture_test.go new file mode 100644 index 000000000000..1c340b90faa4 --- /dev/null +++ b/azurerm/resource_arm_packet_capture_test.go @@ -0,0 +1,344 @@ +package azurerm + +import ( + "fmt" + "net/http" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func testAccAzureRMPacketCapture_localDisk(t *testing.T) { + resourceName := "azurerm_packet_capture.test" + + ri := acctest.RandInt() + location := testLocation() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMPacketCaptureDestroy, + Steps: []resource.TestStep{ + { + Config: testAzureRMPacketCapture_localDiskConfig(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMPacketCaptureExists(resourceName), + ), + }, + }, + }) +} + +func testAccAzureRMPacketCapture_storageAccount(t *testing.T) { + resourceName := "azurerm_packet_capture.test" + + ri := acctest.RandInt() + rs := acctest.RandString(5) + location := testLocation() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMPacketCaptureDestroy, + Steps: []resource.TestStep{ + { + Config: testAzureRMPacketCapture_storageAccountConfig(ri, rs, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMPacketCaptureExists(resourceName), + ), + }, + }, + }) +} + +func testAccAzureRMPacketCapture_storageAccountAndLocalDisk(t *testing.T) { + resourceName := "azurerm_packet_capture.test" + + ri := acctest.RandInt() + rs := acctest.RandString(5) + location := testLocation() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMPacketCaptureDestroy, + Steps: []resource.TestStep{ + { + Config: testAzureRMPacketCapture_storageAccountAndLocalDiskConfig(ri, rs, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMPacketCaptureExists(resourceName), + ), + }, + }, + }) +} + +func testAccAzureRMPacketCapture_withFilters(t *testing.T) { + resourceName := "azurerm_packet_capture.test" + + ri := acctest.RandInt() + location := testLocation() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMPacketCaptureDestroy, + Steps: []resource.TestStep{ + { + Config: testAzureRMPacketCapture_localDiskConfigWithFilters(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMPacketCaptureExists(resourceName), + ), + }, + }, + }) +} + +func testCheckAzureRMPacketCaptureExists(name string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[name] + if !ok { + return fmt.Errorf("not found: %s", name) + } + + resourceGroup := rs.Primary.Attributes["resource_group_name"] + watcherName := rs.Primary.Attributes["network_watcher_name"] + packetCaptureName := rs.Primary.Attributes["name"] + + client := testAccProvider.Meta().(*ArmClient).packetCapturesClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + resp, err := client.Get(ctx, resourceGroup, watcherName, packetCaptureName) + if err != nil { + return fmt.Errorf("Bad: Get on packetCapturesClient: %s", err) + } + + if resp.StatusCode == http.StatusNotFound { + return fmt.Errorf("Packet Capture does not exist: %s", name) + } + + return nil + } +} + +func testCheckAzureRMPacketCaptureDestroy(s *terraform.State) error { + client := testAccProvider.Meta().(*ArmClient).packetCapturesClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + for _, rs := range s.RootModule().Resources { + if rs.Type != "azurerm_packet_capture" { + continue + } + + resourceGroup := rs.Primary.Attributes["resource_group_name"] + watcherName := rs.Primary.Attributes["network_watcher_name"] + packetCaptureName := rs.Primary.Attributes["name"] + + resp, err := client.Get(ctx, resourceGroup, watcherName, packetCaptureName) + + if err != nil { + return nil + } + + if resp.StatusCode != http.StatusNotFound { + return fmt.Errorf("Packet Capture still exists:%s", *resp.Name) + } + } + + return nil +} + +func testAzureRMPacketCapture_base(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestrg-%d" + location = "%s" +} + +resource "azurerm_network_watcher" "test" { + name = "acctestnw-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" +} + +resource "azurerm_virtual_network" "test" { + name = "acctvn-%d" + address_space = ["10.0.0.0/16"] + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" +} + +resource "azurerm_subnet" "test" { + name = "internal" + resource_group_name = "${azurerm_resource_group.test.name}" + virtual_network_name = "${azurerm_virtual_network.test.name}" + address_prefix = "10.0.2.0/24" +} + +resource "azurerm_network_interface" "test" { + name = "acctni-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + + ip_configuration { + name = "testconfiguration1" + subnet_id = "${azurerm_subnet.test.id}" + private_ip_address_allocation = "dynamic" + } +} + +resource "azurerm_virtual_machine" "test" { + name = "acctvm-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + network_interface_ids = ["${azurerm_network_interface.test.id}"] + vm_size = "Standard_F2" + + storage_image_reference { + publisher = "Canonical" + offer = "UbuntuServer" + sku = "16.04-LTS" + version = "latest" + } + + storage_os_disk { + name = "osdisk" + caching = "ReadWrite" + create_option = "FromImage" + managed_disk_type = "Standard_LRS" + } + + os_profile { + computer_name = "hostname%d" + admin_username = "testadmin" + admin_password = "Password1234!" + } + + os_profile_linux_config { + disable_password_authentication = false + } +} + +resource "azurerm_virtual_machine_extension" "test" { + name = "network-watcher" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + virtual_machine_name = "${azurerm_virtual_machine.test.name}" + publisher = "Microsoft.Azure.NetworkWatcher" + type = "NetworkWatcherAgentLinux" + type_handler_version = "1.4" + auto_upgrade_minor_version = true +} +`, rInt, location, rInt, rInt, rInt, rInt, rInt) +} + +func testAzureRMPacketCapture_localDiskConfig(rInt int, location string) string { + config := testAzureRMPacketCapture_base(rInt, location) + return fmt.Sprintf(` +%s + +resource "azurerm_packet_capture" "test" { + name = "acctestpc-%d" + network_watcher_name = "${azurerm_network_watcher.test.name}" + resource_group_name = "${azurerm_resource_group.test.name}" + target_resource_id = "${azurerm_virtual_machine.test.id}" + + storage_location { + file_path = "/var/captures/packet.cap" + } + + depends_on = ["azurerm_virtual_machine_extension.test"] +} +`, config, rInt) +} + +func testAzureRMPacketCapture_localDiskConfigWithFilters(rInt int, location string) string { + config := testAzureRMPacketCapture_base(rInt, location) + return fmt.Sprintf(` +%s + +resource "azurerm_packet_capture" "test" { + name = "acctestpc-%d" + network_watcher_name = "${azurerm_network_watcher.test.name}" + resource_group_name = "${azurerm_resource_group.test.name}" + target_resource_id = "${azurerm_virtual_machine.test.id}" + + storage_location { + file_path = "/var/captures/packet.cap" + } + + filter { + local_ip_address = "127.0.0.1" + local_port = "8080;9020;" + protocol = "TCP" + } + + filter { + local_ip_address = "127.0.0.1" + local_port = "80;443;" + protocol = "UDP" + } + + depends_on = ["azurerm_virtual_machine_extension.test"] +} +`, config, rInt) +} + +func testAzureRMPacketCapture_storageAccountConfig(rInt int, rString string, location string) string { + config := testAzureRMPacketCapture_base(rInt, location) + return fmt.Sprintf(` +%s + +resource "azurerm_storage_account" "test" { + name = "acctestsa%s" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" + account_tier = "Standard" + account_replication_type = "LRS" +} + +resource "azurerm_packet_capture" "test" { + name = "acctestpc-%d" + network_watcher_name = "${azurerm_network_watcher.test.name}" + resource_group_name = "${azurerm_resource_group.test.name}" + target_resource_id = "${azurerm_virtual_machine.test.id}" + + storage_location { + storage_account_id = "${azurerm_storage_account.test.id}" + } + + depends_on = ["azurerm_virtual_machine_extension.test"] +} +`, config, rString, rInt) +} + +func testAzureRMPacketCapture_storageAccountAndLocalDiskConfig(rInt int, rString string, location string) string { + config := testAzureRMPacketCapture_base(rInt, location) + return fmt.Sprintf(` +%s + +resource "azurerm_storage_account" "test" { + name = "acctestsa%s" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" + account_tier = "Standard" + account_replication_type = "LRS" +} + +resource "azurerm_packet_capture" "test" { + name = "acctestpc-%d" + network_watcher_name = "${azurerm_network_watcher.test.name}" + resource_group_name = "${azurerm_resource_group.test.name}" + target_resource_id = "${azurerm_virtual_machine.test.id}" + + storage_location { + file_path = "/var/captures/packet.cap" + storage_account_id = "${azurerm_storage_account.test.id}" + } + + depends_on = ["azurerm_virtual_machine_extension.test"] +} +`, config, rString, rInt) +} diff --git a/website/azurerm.erb b/website/azurerm.erb index a52abaebd877..db012fca425c 100644 --- a/website/azurerm.erb +++ b/website/azurerm.erb @@ -568,6 +568,10 @@ azurerm_network_watcher + > + azurerm_packet_capture + + > azurerm_public_ip diff --git a/website/docs/r/packet_capture.html.markdown b/website/docs/r/packet_capture.html.markdown new file mode 100644 index 000000000000..11aa261e8b26 --- /dev/null +++ b/website/docs/r/packet_capture.html.markdown @@ -0,0 +1,188 @@ +--- +layout: "azurerm" +page_title: "Azure Resource Manager: azurerm_packet_capture" +sidebar_current: "docs-azurerm-resource-network-packet-capture" +description: |- + Configures Packet Capturing against a Virtual Machine using a Network Watcher. + +--- + +# azurerm_packet_capture + +Configures Packet Capturing against a Virtual Machine using a Network Watcher. + +## Example Usage + +```hcl +resource "azurerm_resource_group" "test" { + name = "packet-capture-rg" + location = "West Europe" +} + +resource "azurerm_network_watcher" "test" { + name = "network-watcher" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" +} + +resource "azurerm_virtual_network" "test" { + name = "production-network" + address_space = ["10.0.0.0/16"] + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" +} + +resource "azurerm_subnet" "test" { + name = "internal" + resource_group_name = "${azurerm_resource_group.test.name}" + virtual_network_name = "${azurerm_virtual_network.test.name}" + address_prefix = "10.0.2.0/24" +} + +resource "azurerm_network_interface" "test" { + name = "pctest-nic" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + + ip_configuration { + name = "testconfiguration1" + subnet_id = "${azurerm_subnet.test.id}" + private_ip_address_allocation = "dynamic" + } +} + +resource "azurerm_virtual_machine" "test" { + name = "pctest-vm" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + network_interface_ids = ["${azurerm_network_interface.test.id}"] + vm_size = "Standard_F2" + + storage_image_reference { + publisher = "Canonical" + offer = "UbuntuServer" + sku = "16.04-LTS" + version = "latest" + } + + storage_os_disk { + name = "osdisk" + caching = "ReadWrite" + create_option = "FromImage" + managed_disk_type = "Standard_LRS" + } + + os_profile { + computer_name = "pctest-vm" + admin_username = "testadmin" + admin_password = "Password1234!" + } + + os_profile_linux_config { + disable_password_authentication = false + } +} + +resource "azurerm_virtual_machine_extension" "test" { + name = "network-watcher" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + virtual_machine_name = "${azurerm_virtual_machine.test.name}" + publisher = "Microsoft.Azure.NetworkWatcher" + type = "NetworkWatcherAgentLinux" + type_handler_version = "1.4" + auto_upgrade_minor_version = true +} + + +resource "azurerm_storage_account" "test" { + name = "pctestsa" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" + account_tier = "Standard" + account_replication_type = "LRS" +} + +resource "azurerm_packet_capture" "test" { + name = "pctestcapture" + network_watcher_name = "${azurerm_network_watcher.test.name}" + resource_group_name = "${azurerm_resource_group.test.name}" + target_resource_id = "${azurerm_virtual_machine.test.id}" + + storage_location { + storage_account_id = "${azurerm_storage_account.test.id}" + } + + depends_on = ["azurerm_virtual_machine_extension.test"] +} +``` + +~> **NOTE:** This Resource requires that [the Network Watcher Virtual Machine Extension](https://docs.microsoft.com/azure/network-watcher/network-watcher-packet-capture-manage-portal#before-you-begin) is installed on the Virtual Machine before capturing can be enabled which can be installed via [the `azurerm_virtual_machine_extension` resource](virtual_machine_extension.html). + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) The name to use for this Packet Capture. Changing this forces a new resource to be created. + +* `network_watcher_name` - (Required) The name of the Network Watcher. Changing this forces a new resource to be created. + +* `resource_group_name` - (Required) The name of the resource group in which the Network Watcher exists. Changing this forces a new resource to be created. + +* `target_resource_id` - (Required) The ID of the Resource to capture packets from. Changing this forces a new resource to be created. + +~> **NOTE:** Currently only Virtual Machines ID's are supported. + +* `maximum_bytes_per_packet` - (Optional) The number of bytes captured per packet. The remaining bytes are truncated. Defaults to `0` (Entire Packet Captured). Changing this forces a new resource to be created. + +* `maximum_bytes_per_session` - (Optional) Maximum size of the capture in Bytes. Defaults to `10737441824` (1GB). Changing this forces a new resource to be created. + +* `maximum_capture_duration` - (Optional) The maximum duration of the capture session in seconds. Defaults to `18000` (5 hours). Changing this forces a new resource to be created. + +* `storage_location` - (Required) A `storage_location` block as defined below. Changing this forces a new resource to be created. + +* `filter` - (Optional) One or more `filter` blocks as defined below. Changing this forces a new resource to be created. + +--- + +A `storage_location` block contains: + +* `file_path` - (Optional) A valid local path on the targeting VM. Must include the name of the capture file (*.cap). For linux virtual machine it must start with `/var/captures`. + +* `storage_account_id` - (Optional) The ID of the storage account to save the packet capture session + +~> **NOTE:** At least one of `file_path` or `storage_account_id` must be specified. + +A `filter` block contains: + +* `local_ip_address` - (Optional) The local IP Address to be filtered on. Notation: "127.0.0.1" for single address entry. "127.0.0.1-127.0.0.255" for range. "127.0.0.1;127.0.0.5"? for multiple entries. Multiple ranges not currently supported. Mixing ranges with multiple entries not currently supported. Changing this forces a new resource to be created. + +* `local_port` - (Optional) The local port to be filtered on. Notation: "80" for single port entry."80-85" for range. "80;443;" for multiple entries. Multiple ranges not currently supported. Mixing ranges with multiple entries not currently supported. Changing this forces a new resource to be created. + +* `protocol` - (Required) The Protocol to be filtered on. Possible values include `Any`, `TCP` and ``UDP`. Changing this forces a new resource to be created. + +* `remote_ip_address` - (Optional) The remote IP Address to be filtered on. Notation: "127.0.0.1" for single address entry. "127.0.0.1-127.0.0.255" for range. "127.0.0.1;127.0.0.5;" for multiple entries. Multiple ranges not currently supported. Mixing ranges with multiple entries not currently supported.. Changing this forces a new resource to be created. + +* `remote_port` - (Optional) The remote port to be filtered on. Notation: "80" for single port entry."80-85" for range. "80;443;" for multiple entries. Multiple ranges not currently supported. Mixing ranges with multiple entries not currently supported. Changing this forces a new resource to be created. + +## Attributes Reference + +The following attributes are exported: + +* `id` - The Packet Capture ID. + +* `storage_location` - (Required) A `storage_location` block as defined below. + +--- + +A `storage_location` block contains: + +* `storage_path` - The URI of the storage path to save the packet capture. + +## Import + +Packet Captures can be imported using the `resource id`, e.g. + +```shell +terraform import azurerm_packet_capture.capture1 /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/mygroup1/providers/Microsoft.Network/networkWatchers/watcher1/packetCaptures/capture1 +```