diff --git a/CHANGELOG.md b/CHANGELOG.md index d642690d7f66..893d50d4ced7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,15 +2,27 @@ FEATURES: -* `azurerm_container_group` - added `dns_name_label` and `FQDN` properties [GH-877] -* `azurerm_servicebus_subscription` - added support for the `forward_to` property [GH-861] -* `azurerm_storage_account` - adding support for `account_kind` being `StorageV2` [GH-851] +* **New Data Source:** `azurerm_application_security_group` [GH-914] +* **New Resource:** `azurerm_application_security_group` [GH-905] +* **New Resource:** `azurerm_servicebus_topic_authorization_rule` [GH-736] BUG FIXES: +* `azurerm_kubernetes_cluster` - an empty `linux_profile.ssh_key.keydata` no longer causes a crash [GH-903] +* `azurerm_kubernetes_cluster` - the `linux_profile.admin_username` and `linux_profile.ssh_key.keydata` fields now force a new resource [GH-895] * `azurerm_virtual_machine_scale_set` - the `computer_name_prefix` field now forces a new resource [GH-871] * `azurerm_network_interface` - the `subnet_id` field is now case insensitive [GH-866] +IMPROVEMENTS: + +* authentication: adding support for Managed Service Identity [GH-639] +* `azurerm_container_group` - added `dns_name_label` and `FQDN` properties [GH-877] +* `azurerm_network_interface` - support for attaching to Application Security Groups [GH-911] +* `azurerm_network_security_group` - support for augmented security rules [GH-781] +* `azurerm_servicebus_subscription` - added support for the `forward_to` property [GH-861] +* `azurerm_storage_account` - adding support for `account_kind` being `StorageV2` [GH-851] +* `azurerm_virtual_network_gateway_connection` - support for IPsec/IKE Policies [GH-834] + ## 1.1.2 (February 19, 2018) FEATURES: diff --git a/azurerm/config.go b/azurerm/config.go index c1297a9c2a48..69e84c52b2e6 100644 --- a/azurerm/config.go +++ b/azurerm/config.go @@ -133,23 +133,24 @@ type ArmClient struct { monitorAlertRulesClient insights.AlertRulesClient // Networking - applicationGatewayClient network.ApplicationGatewaysClient - expressRouteCircuitClient network.ExpressRouteCircuitsClient - ifaceClient network.InterfacesClient - loadBalancerClient network.LoadBalancersClient - localNetConnClient network.LocalNetworkGatewaysClient - publicIPClient network.PublicIPAddressesClient - routesClient network.RoutesClient - routeTablesClient network.RouteTablesClient - secGroupClient network.SecurityGroupsClient - secRuleClient network.SecurityRulesClient - subnetClient network.SubnetsClient - netUsageClient network.UsagesClient - vnetGatewayConnectionsClient network.VirtualNetworkGatewayConnectionsClient - vnetGatewayClient network.VirtualNetworkGatewaysClient - vnetClient network.VirtualNetworksClient - vnetPeeringsClient network.VirtualNetworkPeeringsClient - watcherClient network.WatchersClient + applicationGatewayClient network.ApplicationGatewaysClient + applicationSecurityGroupsClient network.ApplicationSecurityGroupsClient + expressRouteCircuitClient network.ExpressRouteCircuitsClient + ifaceClient network.InterfacesClient + loadBalancerClient network.LoadBalancersClient + localNetConnClient network.LocalNetworkGatewaysClient + publicIPClient network.PublicIPAddressesClient + routesClient network.RoutesClient + routeTablesClient network.RouteTablesClient + secGroupClient network.SecurityGroupsClient + secRuleClient network.SecurityRulesClient + subnetClient network.SubnetsClient + netUsageClient network.UsagesClient + vnetGatewayConnectionsClient network.VirtualNetworkGatewayConnectionsClient + vnetGatewayClient network.VirtualNetworkGatewaysClient + vnetClient network.VirtualNetworksClient + vnetPeeringsClient network.VirtualNetworkPeeringsClient + watcherClient network.WatchersClient // Resources managementLocksClient locks.ManagementLocksClient @@ -246,6 +247,15 @@ func getAuthorizationToken(c *authentication.Config, oauthConfig *adal.OAuthConf return auth, nil } + if c.UseMsi { + spt, err := adal.NewServicePrincipalTokenFromMSI(c.MsiEndpoint, endpoint) + if err != nil { + return nil, err + } + auth := autorest.NewBearerAuthorizer(spt) + return auth, nil + } + if c.IsCloudShell { // load the refreshed tokens from the Azure CLI err := c.LoadTokensFromAzureCLI() @@ -323,22 +333,15 @@ func getArmClient(c *authentication.Config) (*ArmClient, error) { return keyVaultSpt, nil }) - csc := containerservice.NewContainerServicesClientWithBaseURI(endpoint, c.SubscriptionID) - setUserAgent(&csc.Client) - csc.Authorizer = auth - csc.Sender = sender - csc.SkipResourceProviderRegistration = c.SkipProviderRegistration - client.containerServicesClient = csc - client.registerAppInsightsClients(endpoint, c.SubscriptionID, auth, sender) client.registerAutomationClients(endpoint, c.SubscriptionID, auth, sender) client.registerAuthentication(endpoint, graphEndpoint, c.SubscriptionID, c.TenantID, auth, graphAuth, sender) client.registerCDNClients(endpoint, c.SubscriptionID, auth, sender) client.registerComputeClients(endpoint, c.SubscriptionID, auth, sender) - client.registerContainerServicesClients(endpoint, c.SubscriptionID, auth) - client.registerCosmosDBClients(endpoint, c.SubscriptionID, auth, sender) client.registerContainerInstanceClients(endpoint, c.SubscriptionID, auth, sender) client.registerContainerRegistryClients(endpoint, c.SubscriptionID, auth, sender) + client.registerContainerServicesClients(endpoint, c.SubscriptionID, auth) + client.registerCosmosDBClients(endpoint, c.SubscriptionID, auth, sender) client.registerDatabases(endpoint, c.SubscriptionID, auth, sender) client.registerDNSClients(endpoint, c.SubscriptionID, auth, sender) client.registerEventGridClients(endpoint, c.SubscriptionID, auth, sender) @@ -656,6 +659,10 @@ func (c *ArmClient) registerNetworkingClients(endpoint, subscriptionId string, a c.configureClient(&applicationGatewaysClient.Client, auth) c.applicationGatewayClient = applicationGatewaysClient + appSecurityGroupsClient := network.NewApplicationSecurityGroupsClientWithBaseURI(endpoint, subscriptionId) + c.configureClient(&appSecurityGroupsClient.Client, auth) + c.applicationSecurityGroupsClient = appSecurityGroupsClient + expressRouteCircuitsClient := network.NewExpressRouteCircuitsClientWithBaseURI(endpoint, subscriptionId) c.configureClient(&expressRouteCircuitsClient.Client, auth) c.expressRouteCircuitClient = expressRouteCircuitsClient diff --git a/azurerm/data_source_application_security_group.go b/azurerm/data_source_application_security_group.go new file mode 100644 index 000000000000..f692283be7fb --- /dev/null +++ b/azurerm/data_source_application_security_group.go @@ -0,0 +1,57 @@ +package azurerm + +import ( + "fmt" + + "github.com/hashicorp/terraform/helper/schema" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func dataSourceArmApplicationSecurityGroup() *schema.Resource { + return &schema.Resource{ + Read: dataSourceArmApplicationSecurityGroupRead, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + }, + + "location": locationForDataSourceSchema(), + + "resource_group_name": resourceGroupNameForDataSourceSchema(), + + "tags": tagsForDataSourceSchema(), + }, + } +} + +func dataSourceArmApplicationSecurityGroupRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).applicationSecurityGroupsClient + ctx := meta.(*ArmClient).StopContext + + resourceGroup := d.Get("resource_group_name").(string) + name := d.Get("name").(string) + + resp, err := client.Get(ctx, resourceGroup, name) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + d.SetId("") + return nil + } + + return fmt.Errorf("Error making Read request on Application Security Group %q (Resource Group %q): %+v", name, resourceGroup, err) + } + + d.SetId(*resp.ID) + + d.Set("name", resp.Name) + d.Set("location", azureRMNormalizeLocation(*resp.Location)) + d.Set("resource_group_name", resourceGroup) + flattenAndSetTags(d, resp.Tags) + + return nil +} diff --git a/azurerm/data_source_application_security_group_test.go b/azurerm/data_source_application_security_group_test.go new file mode 100644 index 000000000000..70006689911e --- /dev/null +++ b/azurerm/data_source_application_security_group_test.go @@ -0,0 +1,93 @@ +package azurerm + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" +) + +func TestAccDataSourceAzureRMApplicationSecurityGroup_basic(t *testing.T) { + dataSourceName := "data.azurerm_application_security_group.test" + ri := acctest.RandInt() + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccDataSourceApplicationSecurityGroup_basic(ri, testLocation()), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(dataSourceName, "location"), + resource.TestCheckResourceAttrSet(dataSourceName, "name"), + resource.TestCheckResourceAttrSet(dataSourceName, "resource_group_name"), + resource.TestCheckResourceAttr(dataSourceName, "tags.%", "0"), + ), + }, + }, + }) +} + +func TestAccDataSourceAzureRMApplicationSecurityGroup_complete(t *testing.T) { + dataSourceName := "data.azurerm_application_security_group.test" + ri := acctest.RandInt() + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccDataSourceApplicationSecurityGroup_complete(ri, testLocation()), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(dataSourceName, "location"), + resource.TestCheckResourceAttrSet(dataSourceName, "name"), + resource.TestCheckResourceAttrSet(dataSourceName, "resource_group_name"), + resource.TestCheckResourceAttr(dataSourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(dataSourceName, "tags.Hello", "World"), + ), + }, + }, + }) +} + +func testAccDataSourceApplicationSecurityGroup_basic(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_application_security_group" "test" { + name = "acctest-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" +} + +data "azurerm_application_security_group" "test" { + name = "${azurerm_application_security_group.test.name}" + resource_group_name = "${azurerm_resource_group.test.name}" +} +`, rInt, location, rInt) +} + +func testAccDataSourceApplicationSecurityGroup_complete(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_application_security_group" "test" { + name = "acctest-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + tags { + "Hello" = "World" + } +} + +data "azurerm_application_security_group" "test" { + name = "${azurerm_application_security_group.test.name}" + resource_group_name = "${azurerm_resource_group.test.name}" +} +`, rInt, location, rInt) +} diff --git a/azurerm/data_source_network_security_group.go b/azurerm/data_source_network_security_group.go index 9c981f511802..6f278480a6d3 100644 --- a/azurerm/data_source_network_security_group.go +++ b/azurerm/data_source_network_security_group.go @@ -105,7 +105,10 @@ func dataSourceArmNetworkSecurityGroupRead(d *schema.ResourceData, meta interfac d.Set("location", azureRMNormalizeLocation(*resp.Location)) if props := resp.SecurityGroupPropertiesFormat; props != nil { - d.Set("security_rule", flattenNetworkSecurityRules(props.SecurityRules)) + flattenedRules := flattenNetworkSecurityRules(props.SecurityRules) + if err := d.Set("security_rule", flattenedRules); err != nil { + return fmt.Errorf("Error flattening `security_rule`: %+v", err) + } } flattenAndSetTags(d, resp.Tags) diff --git a/azurerm/helpers/authentication/config.go b/azurerm/helpers/authentication/config.go index d70acb47ee5c..3dac57a521fd 100644 --- a/azurerm/helpers/authentication/config.go +++ b/azurerm/helpers/authentication/config.go @@ -28,6 +28,8 @@ type Config struct { // Bearer Auth AccessToken *adal.Token IsCloudShell bool + UseMsi bool + MsiEndpoint string } func (c *Config) LoadTokensFromAzureCLI() error { diff --git a/azurerm/helpers/authentication/validation.go b/azurerm/helpers/authentication/validation.go index 8c777145a2cd..460441065515 100644 --- a/azurerm/helpers/authentication/validation.go +++ b/azurerm/helpers/authentication/validation.go @@ -49,3 +49,22 @@ func (c *Config) ValidateServicePrincipal() error { return err.ErrorOrNil() } + +func (c *Config) ValidateMsi() error { + var err *multierror.Error + + if c.SubscriptionID == "" { + err = multierror.Append(err, fmt.Errorf("Subscription ID must be configured for the AzureRM provider")) + } + if c.TenantID == "" { + err = multierror.Append(err, fmt.Errorf("Tenant ID must be configured for the AzureRM provider")) + } + if c.Environment == "" { + err = multierror.Append(err, fmt.Errorf("Environment must be configured for the AzureRM provider")) + } + if c.MsiEndpoint == "" { + err = multierror.Append(err, fmt.Errorf("MSI endpoint must be configured for the AzureRM provider")) + } + + return err.ErrorOrNil() +} diff --git a/azurerm/helpers/authentication/validation_test.go b/azurerm/helpers/authentication/validation_test.go index 99cab000e609..f69594b4bad1 100644 --- a/azurerm/helpers/authentication/validation_test.go +++ b/azurerm/helpers/authentication/validation_test.go @@ -164,3 +164,75 @@ func TestAzureValidateServicePrincipal(t *testing.T) { } } } + +func TestAzureValidateMsi(t *testing.T) { + cases := []struct { + Description string + Config Config + ExpectError bool + }{ + { + Description: "Empty Configuration", + Config: Config{}, + ExpectError: true, + }, + { + Description: "Missing Subscription ID", + Config: Config{ + MsiEndpoint: "http://localhost:50342/oauth2/token", + TenantID: "9834f8d0-24b3-41b7-8b8d-c611c461a129", + Environment: "public", + }, + ExpectError: true, + }, + { + Description: "Missing Tenant ID", + Config: Config{ + MsiEndpoint: "http://localhost:50342/oauth2/token", + SubscriptionID: "8e8b5e02-5c13-4822-b7dc-4232afb7e8c2", + Environment: "public", + }, + ExpectError: true, + }, + { + Description: "Missing Environment", + Config: Config{ + MsiEndpoint: "http://localhost:50342/oauth2/token", + SubscriptionID: "8e8b5e02-5c13-4822-b7dc-4232afb7e8c2", + TenantID: "9834f8d0-24b3-41b7-8b8d-c611c461a129", + }, + ExpectError: true, + }, + { + Description: "Missing MSI Endpoint", + Config: Config{ + SubscriptionID: "8e8b5e02-5c13-4822-b7dc-4232afb7e8c2", + TenantID: "9834f8d0-24b3-41b7-8b8d-c611c461a129", + Environment: "public", + }, + ExpectError: true, + }, + { + Description: "Valid Configuration", + Config: Config{ + MsiEndpoint: "http://localhost:50342/oauth2/token", + SubscriptionID: "8e8b5e02-5c13-4822-b7dc-4232afb7e8c2", + TenantID: "9834f8d0-24b3-41b7-8b8d-c611c461a129", + Environment: "public", + }, + ExpectError: false, + }, + } + + for _, v := range cases { + err := v.Config.ValidateMsi() + + if v.ExpectError && err == nil { + t.Fatalf("Expected an error for %q: didn't get one", v.Description) + } + + if !v.ExpectError && err != nil { + t.Fatalf("Expected there to be no error for %q - but got: %v", v.Description, err) + } + } +} diff --git a/azurerm/import_arm_network_interface_test.go b/azurerm/import_arm_network_interface_test.go index a80ec97d25dc..193764c236dc 100644 --- a/azurerm/import_arm_network_interface_test.go +++ b/azurerm/import_arm_network_interface_test.go @@ -111,3 +111,24 @@ func TestAccAzureRMNetworkInterface_importPublicIP(t *testing.T) { }, }) } + +func TestAccAzureRMNetworkInterface_importApplicationSecurityGroup(t *testing.T) { + resourceName := "azurerm_network_interface.test" + rInt := acctest.RandInt() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMNetworkInterfaceDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMNetworkInterface_applicationSecurityGroup(rInt, testLocation()), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} diff --git a/azurerm/import_arm_servicebus_topic_authorization_rule_test.go b/azurerm/import_arm_servicebus_topic_authorization_rule_test.go new file mode 100644 index 000000000000..0eb3a0d3a348 --- /dev/null +++ b/azurerm/import_arm_servicebus_topic_authorization_rule_test.go @@ -0,0 +1,104 @@ +package azurerm + +import ( + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" +) + +func TestAccAzureRMServiceBusTopicAuthorizationRule_importListen(t *testing.T) { + resourceName := "azurerm_servicebus_topic_authorization_rule.test" + + ri := acctest.RandInt() + config := testAccAzureRMServiceBusTopicAuthorizationRule_listen(ri, testLocation()) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMServiceBusTopicAuthorizationRuleDestroy, + Steps: []resource.TestStep{ + { + Config: config, + }, + + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureRMServiceBusTopicAuthorizationRule_importSend(t *testing.T) { + resourceName := "azurerm_servicebus_topic_authorization_rule.test" + + ri := acctest.RandInt() + config := testAccAzureRMServiceBusTopicAuthorizationRule_send(ri, testLocation()) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMServiceBusTopicAuthorizationRuleDestroy, + Steps: []resource.TestStep{ + { + Config: config, + }, + + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureRMServiceBusTopicAuthorizationRule_importReadWrite(t *testing.T) { + resourceName := "azurerm_servicebus_topic_authorization_rule.test" + + ri := acctest.RandInt() + config := testAccAzureRMServiceBusTopicAuthorizationRule_readWrite(ri, testLocation()) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMServiceBusTopicAuthorizationRuleDestroy, + Steps: []resource.TestStep{ + { + Config: config, + }, + + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureRMServiceBusTopicAuthorizationRule_importManage(t *testing.T) { + resourceName := "azurerm_servicebus_topic_authorization_rule.test" + + ri := acctest.RandInt() + config := testAccAzureRMServiceBusTopicAuthorizationRule_manage(ri, testLocation()) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMServiceBusTopicAuthorizationRuleDestroy, + Steps: []resource.TestStep{ + { + Config: config, + }, + + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} diff --git a/azurerm/network_security_rule.go b/azurerm/network_security_rule.go deleted file mode 100644 index c9df9ee516f1..000000000000 --- a/azurerm/network_security_rule.go +++ /dev/null @@ -1,20 +0,0 @@ -package azurerm - -import ( - "fmt" - "strings" -) - -func validateNetworkSecurityRuleProtocol(v interface{}, k string) (ws []string, errors []error) { - value := strings.ToLower(v.(string)) - protocols := map[string]bool{ - "tcp": true, - "udp": true, - "*": true, - } - - if !protocols[value] { - errors = append(errors, fmt.Errorf("Network Security Rule Protocol can only be Tcp, Udp or *")) - } - return -} diff --git a/azurerm/network_security_rule_test.go b/azurerm/network_security_rule_test.go deleted file mode 100644 index 8b9f21ecc4b2..000000000000 --- a/azurerm/network_security_rule_test.go +++ /dev/null @@ -1,43 +0,0 @@ -package azurerm - -import "testing" - -func TestResourceAzureRMNetworkSecurityRuleProtocol_validation(t *testing.T) { - cases := []struct { - Value string - ErrCount int - }{ - { - Value: "Random", - ErrCount: 1, - }, - { - Value: "tcp", - ErrCount: 0, - }, - { - Value: "TCP", - ErrCount: 0, - }, - { - Value: "*", - ErrCount: 0, - }, - { - Value: "Udp", - ErrCount: 0, - }, - { - Value: "Tcp", - ErrCount: 0, - }, - } - - for _, tc := range cases { - _, errors := validateNetworkSecurityRuleProtocol(tc.Value, "azurerm_network_security_rule") - - if len(errors) != tc.ErrCount { - t.Fatalf("Expected the Azure RM Network Security Rule protocol to trigger a validation error") - } - } -} diff --git a/azurerm/provider.go b/azurerm/provider.go index e863b5794630..9bc8e0c49480 100644 --- a/azurerm/provider.go +++ b/azurerm/provider.go @@ -11,6 +11,7 @@ import ( "sync" "github.com/Azure/azure-sdk-for-go/services/resources/mgmt/2017-05-10/resources" + "github.com/Azure/go-autorest/autorest/adal" "github.com/hashicorp/terraform/helper/mutexkv" "github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/terraform" @@ -63,128 +64,141 @@ func Provider() terraform.ResourceProvider { Optional: true, DefaultFunc: schema.EnvDefaultFunc("ARM_SKIP_PROVIDER_REGISTRATION", false), }, + "use_msi": { + Type: schema.TypeBool, + Optional: true, + DefaultFunc: schema.EnvDefaultFunc("ARM_USE_MSI", false), + }, + "msi_endpoint": { + Type: schema.TypeString, + Optional: true, + DefaultFunc: schema.EnvDefaultFunc("ARM_MSI_ENDPOINT", ""), + }, }, DataSourcesMap: map[string]*schema.Resource{ - "azurerm_app_service_plan": dataSourceAppServicePlan(), - "azurerm_builtin_role_definition": dataSourceArmBuiltInRoleDefinition(), - "azurerm_client_config": dataSourceArmClientConfig(), - "azurerm_dns_zone": dataSourceArmDnsZone(), - "azurerm_eventhub_namespace": dataSourceEventHubNamespace(), - "azurerm_image": dataSourceArmImage(), - "azurerm_key_vault_access_policy": dataSourceArmKeyVaultAccessPolicy(), - "azurerm_managed_disk": dataSourceArmManagedDisk(), - "azurerm_network_security_group": dataSourceArmNetworkSecurityGroup(), - "azurerm_platform_image": dataSourceArmPlatformImage(), - "azurerm_public_ip": dataSourceArmPublicIP(), - "azurerm_resource_group": dataSourceArmResourceGroup(), - "azurerm_role_definition": dataSourceArmRoleDefinition(), - "azurerm_storage_account": dataSourceArmStorageAccount(), - "azurerm_snapshot": dataSourceArmSnapshot(), - "azurerm_subnet": dataSourceArmSubnet(), - "azurerm_subscription": dataSourceArmSubscription(), - "azurerm_virtual_network": dataSourceArmVirtualNetwork(), - "azurerm_virtual_network_gateway": dataSourceArmVirtualNetworkGateway(), + "azurerm_application_security_group": dataSourceArmApplicationSecurityGroup(), + "azurerm_app_service_plan": dataSourceAppServicePlan(), + "azurerm_builtin_role_definition": dataSourceArmBuiltInRoleDefinition(), + "azurerm_client_config": dataSourceArmClientConfig(), + "azurerm_dns_zone": dataSourceArmDnsZone(), + "azurerm_eventhub_namespace": dataSourceEventHubNamespace(), + "azurerm_image": dataSourceArmImage(), + "azurerm_key_vault_access_policy": dataSourceArmKeyVaultAccessPolicy(), + "azurerm_managed_disk": dataSourceArmManagedDisk(), + "azurerm_network_security_group": dataSourceArmNetworkSecurityGroup(), + "azurerm_platform_image": dataSourceArmPlatformImage(), + "azurerm_public_ip": dataSourceArmPublicIP(), + "azurerm_resource_group": dataSourceArmResourceGroup(), + "azurerm_role_definition": dataSourceArmRoleDefinition(), + "azurerm_storage_account": dataSourceArmStorageAccount(), + "azurerm_snapshot": dataSourceArmSnapshot(), + "azurerm_subnet": dataSourceArmSubnet(), + "azurerm_subscription": dataSourceArmSubscription(), + "azurerm_virtual_network": dataSourceArmVirtualNetwork(), + "azurerm_virtual_network_gateway": dataSourceArmVirtualNetworkGateway(), }, ResourcesMap: map[string]*schema.Resource{ - "azurerm_application_gateway": resourceArmApplicationGateway(), - "azurerm_application_insights": resourceArmApplicationInsights(), - "azurerm_app_service": resourceArmAppService(), - "azurerm_app_service_plan": resourceArmAppServicePlan(), - "azurerm_app_service_active_slot": resourceArmAppServiceActiveSlot(), - "azurerm_app_service_slot": resourceArmAppServiceSlot(), - "azurerm_automation_account": resourceArmAutomationAccount(), - "azurerm_automation_credential": resourceArmAutomationCredential(), - "azurerm_automation_runbook": resourceArmAutomationRunbook(), - "azurerm_automation_schedule": resourceArmAutomationSchedule(), - "azurerm_availability_set": resourceArmAvailabilitySet(), - "azurerm_cdn_endpoint": resourceArmCdnEndpoint(), - "azurerm_cdn_profile": resourceArmCdnProfile(), - "azurerm_container_registry": resourceArmContainerRegistry(), - "azurerm_container_service": resourceArmContainerService(), - "azurerm_container_group": resourceArmContainerGroup(), - "azurerm_cosmosdb_account": resourceArmCosmosDBAccount(), - "azurerm_dns_a_record": resourceArmDnsARecord(), - "azurerm_dns_aaaa_record": resourceArmDnsAAAARecord(), - "azurerm_dns_cname_record": resourceArmDnsCNameRecord(), - "azurerm_dns_mx_record": resourceArmDnsMxRecord(), - "azurerm_dns_ns_record": resourceArmDnsNsRecord(), - "azurerm_dns_ptr_record": resourceArmDnsPtrRecord(), - "azurerm_dns_srv_record": resourceArmDnsSrvRecord(), - "azurerm_dns_txt_record": resourceArmDnsTxtRecord(), - "azurerm_dns_zone": resourceArmDnsZone(), - "azurerm_eventgrid_topic": resourceArmEventGridTopic(), - "azurerm_eventhub": resourceArmEventHub(), - "azurerm_eventhub_authorization_rule": resourceArmEventHubAuthorizationRule(), - "azurerm_eventhub_consumer_group": resourceArmEventHubConsumerGroup(), - "azurerm_eventhub_namespace": resourceArmEventHubNamespace(), - "azurerm_express_route_circuit": resourceArmExpressRouteCircuit(), - "azurerm_function_app": resourceArmFunctionApp(), - "azurerm_image": resourceArmImage(), - "azurerm_key_vault": resourceArmKeyVault(), - "azurerm_key_vault_certificate": resourceArmKeyVaultCertificate(), - "azurerm_key_vault_key": resourceArmKeyVaultKey(), - "azurerm_key_vault_secret": resourceArmKeyVaultSecret(), - "azurerm_kubernetes_cluster": resourceArmKubernetesCluster(), - "azurerm_lb": resourceArmLoadBalancer(), - "azurerm_lb_backend_address_pool": resourceArmLoadBalancerBackendAddressPool(), - "azurerm_lb_nat_rule": resourceArmLoadBalancerNatRule(), - "azurerm_lb_nat_pool": resourceArmLoadBalancerNatPool(), - "azurerm_lb_probe": resourceArmLoadBalancerProbe(), - "azurerm_lb_rule": resourceArmLoadBalancerRule(), - "azurerm_local_network_gateway": resourceArmLocalNetworkGateway(), - "azurerm_log_analytics_workspace": resourceArmLogAnalyticsWorkspace(), - "azurerm_managed_disk": resourceArmManagedDisk(), - "azurerm_management_lock": resourceArmManagementLock(), - "azurerm_metric_alertrule": resourceArmMetricAlertRule(), - "azurerm_mysql_configuration": resourceArmMySQLConfiguration(), - "azurerm_mysql_database": resourceArmMySqlDatabase(), - "azurerm_mysql_firewall_rule": resourceArmMySqlFirewallRule(), - "azurerm_mysql_server": resourceArmMySqlServer(), - "azurerm_network_interface": resourceArmNetworkInterface(), - "azurerm_network_security_group": resourceArmNetworkSecurityGroup(), - "azurerm_network_security_rule": resourceArmNetworkSecurityRule(), - "azurerm_network_watcher": resourceArmNetworkWatcher(), - "azurerm_postgresql_configuration": resourceArmPostgreSQLConfiguration(), - "azurerm_postgresql_database": resourceArmPostgreSQLDatabase(), - "azurerm_postgresql_firewall_rule": resourceArmPostgreSQLFirewallRule(), - "azurerm_postgresql_server": resourceArmPostgreSQLServer(), - "azurerm_public_ip": resourceArmPublicIp(), - "azurerm_redis_cache": resourceArmRedisCache(), - "azurerm_redis_firewall_rule": resourceArmRedisFirewallRule(), - "azurerm_resource_group": resourceArmResourceGroup(), - "azurerm_role_assignment": resourceArmRoleAssignment(), - "azurerm_role_definition": resourceArmRoleDefinition(), - "azurerm_route": resourceArmRoute(), - "azurerm_route_table": resourceArmRouteTable(), - "azurerm_search_service": resourceArmSearchService(), - "azurerm_servicebus_namespace": resourceArmServiceBusNamespace(), - "azurerm_servicebus_queue": resourceArmServiceBusQueue(), - "azurerm_servicebus_subscription": resourceArmServiceBusSubscription(), - "azurerm_servicebus_topic": resourceArmServiceBusTopic(), - "azurerm_snapshot": resourceArmSnapshot(), - "azurerm_sql_database": resourceArmSqlDatabase(), - "azurerm_sql_elasticpool": resourceArmSqlElasticPool(), - "azurerm_sql_firewall_rule": resourceArmSqlFirewallRule(), - "azurerm_sql_server": resourceArmSqlServer(), - "azurerm_storage_account": resourceArmStorageAccount(), - "azurerm_storage_blob": resourceArmStorageBlob(), - "azurerm_storage_container": resourceArmStorageContainer(), - "azurerm_storage_share": resourceArmStorageShare(), - "azurerm_storage_queue": resourceArmStorageQueue(), - "azurerm_storage_table": resourceArmStorageTable(), - "azurerm_subnet": resourceArmSubnet(), - "azurerm_template_deployment": resourceArmTemplateDeployment(), - "azurerm_traffic_manager_endpoint": resourceArmTrafficManagerEndpoint(), - "azurerm_traffic_manager_profile": resourceArmTrafficManagerProfile(), - "azurerm_virtual_machine_extension": resourceArmVirtualMachineExtensions(), - "azurerm_virtual_machine": resourceArmVirtualMachine(), - "azurerm_virtual_machine_scale_set": resourceArmVirtualMachineScaleSet(), - "azurerm_virtual_network": resourceArmVirtualNetwork(), - "azurerm_virtual_network_gateway": resourceArmVirtualNetworkGateway(), - "azurerm_virtual_network_gateway_connection": resourceArmVirtualNetworkGatewayConnection(), - "azurerm_virtual_network_peering": resourceArmVirtualNetworkPeering(), + "azurerm_application_gateway": resourceArmApplicationGateway(), + "azurerm_application_insights": resourceArmApplicationInsights(), + "azurerm_application_security_group": resourceArmApplicationSecurityGroup(), + "azurerm_app_service": resourceArmAppService(), + "azurerm_app_service_plan": resourceArmAppServicePlan(), + "azurerm_app_service_active_slot": resourceArmAppServiceActiveSlot(), + "azurerm_app_service_slot": resourceArmAppServiceSlot(), + "azurerm_automation_account": resourceArmAutomationAccount(), + "azurerm_automation_credential": resourceArmAutomationCredential(), + "azurerm_automation_runbook": resourceArmAutomationRunbook(), + "azurerm_automation_schedule": resourceArmAutomationSchedule(), + "azurerm_availability_set": resourceArmAvailabilitySet(), + "azurerm_cdn_endpoint": resourceArmCdnEndpoint(), + "azurerm_cdn_profile": resourceArmCdnProfile(), + "azurerm_container_registry": resourceArmContainerRegistry(), + "azurerm_container_service": resourceArmContainerService(), + "azurerm_container_group": resourceArmContainerGroup(), + "azurerm_cosmosdb_account": resourceArmCosmosDBAccount(), + "azurerm_dns_a_record": resourceArmDnsARecord(), + "azurerm_dns_aaaa_record": resourceArmDnsAAAARecord(), + "azurerm_dns_cname_record": resourceArmDnsCNameRecord(), + "azurerm_dns_mx_record": resourceArmDnsMxRecord(), + "azurerm_dns_ns_record": resourceArmDnsNsRecord(), + "azurerm_dns_ptr_record": resourceArmDnsPtrRecord(), + "azurerm_dns_srv_record": resourceArmDnsSrvRecord(), + "azurerm_dns_txt_record": resourceArmDnsTxtRecord(), + "azurerm_dns_zone": resourceArmDnsZone(), + "azurerm_eventgrid_topic": resourceArmEventGridTopic(), + "azurerm_eventhub": resourceArmEventHub(), + "azurerm_eventhub_authorization_rule": resourceArmEventHubAuthorizationRule(), + "azurerm_eventhub_consumer_group": resourceArmEventHubConsumerGroup(), + "azurerm_eventhub_namespace": resourceArmEventHubNamespace(), + "azurerm_express_route_circuit": resourceArmExpressRouteCircuit(), + "azurerm_function_app": resourceArmFunctionApp(), + "azurerm_image": resourceArmImage(), + "azurerm_key_vault": resourceArmKeyVault(), + "azurerm_key_vault_certificate": resourceArmKeyVaultCertificate(), + "azurerm_key_vault_key": resourceArmKeyVaultKey(), + "azurerm_key_vault_secret": resourceArmKeyVaultSecret(), + "azurerm_kubernetes_cluster": resourceArmKubernetesCluster(), + "azurerm_lb": resourceArmLoadBalancer(), + "azurerm_lb_backend_address_pool": resourceArmLoadBalancerBackendAddressPool(), + "azurerm_lb_nat_rule": resourceArmLoadBalancerNatRule(), + "azurerm_lb_nat_pool": resourceArmLoadBalancerNatPool(), + "azurerm_lb_probe": resourceArmLoadBalancerProbe(), + "azurerm_lb_rule": resourceArmLoadBalancerRule(), + "azurerm_local_network_gateway": resourceArmLocalNetworkGateway(), + "azurerm_log_analytics_workspace": resourceArmLogAnalyticsWorkspace(), + "azurerm_managed_disk": resourceArmManagedDisk(), + "azurerm_management_lock": resourceArmManagementLock(), + "azurerm_metric_alertrule": resourceArmMetricAlertRule(), + "azurerm_mysql_configuration": resourceArmMySQLConfiguration(), + "azurerm_mysql_database": resourceArmMySqlDatabase(), + "azurerm_mysql_firewall_rule": resourceArmMySqlFirewallRule(), + "azurerm_mysql_server": resourceArmMySqlServer(), + "azurerm_network_interface": resourceArmNetworkInterface(), + "azurerm_network_security_group": resourceArmNetworkSecurityGroup(), + "azurerm_network_security_rule": resourceArmNetworkSecurityRule(), + "azurerm_network_watcher": resourceArmNetworkWatcher(), + "azurerm_postgresql_configuration": resourceArmPostgreSQLConfiguration(), + "azurerm_postgresql_database": resourceArmPostgreSQLDatabase(), + "azurerm_postgresql_firewall_rule": resourceArmPostgreSQLFirewallRule(), + "azurerm_postgresql_server": resourceArmPostgreSQLServer(), + "azurerm_public_ip": resourceArmPublicIp(), + "azurerm_redis_cache": resourceArmRedisCache(), + "azurerm_redis_firewall_rule": resourceArmRedisFirewallRule(), + "azurerm_resource_group": resourceArmResourceGroup(), + "azurerm_role_assignment": resourceArmRoleAssignment(), + "azurerm_role_definition": resourceArmRoleDefinition(), + "azurerm_route": resourceArmRoute(), + "azurerm_route_table": resourceArmRouteTable(), + "azurerm_search_service": resourceArmSearchService(), + "azurerm_servicebus_namespace": resourceArmServiceBusNamespace(), + "azurerm_servicebus_queue": resourceArmServiceBusQueue(), + "azurerm_servicebus_subscription": resourceArmServiceBusSubscription(), + "azurerm_servicebus_topic": resourceArmServiceBusTopic(), + "azurerm_servicebus_topic_authorization_rule": resourceArmServiceBusTopicAuthorizationRule(), + "azurerm_snapshot": resourceArmSnapshot(), + "azurerm_sql_database": resourceArmSqlDatabase(), + "azurerm_sql_elasticpool": resourceArmSqlElasticPool(), + "azurerm_sql_firewall_rule": resourceArmSqlFirewallRule(), + "azurerm_sql_server": resourceArmSqlServer(), + "azurerm_storage_account": resourceArmStorageAccount(), + "azurerm_storage_blob": resourceArmStorageBlob(), + "azurerm_storage_container": resourceArmStorageContainer(), + "azurerm_storage_share": resourceArmStorageShare(), + "azurerm_storage_queue": resourceArmStorageQueue(), + "azurerm_storage_table": resourceArmStorageTable(), + "azurerm_subnet": resourceArmSubnet(), + "azurerm_template_deployment": resourceArmTemplateDeployment(), + "azurerm_traffic_manager_endpoint": resourceArmTrafficManagerEndpoint(), + "azurerm_traffic_manager_profile": resourceArmTrafficManagerProfile(), + "azurerm_virtual_machine_extension": resourceArmVirtualMachineExtensions(), + "azurerm_virtual_machine": resourceArmVirtualMachine(), + "azurerm_virtual_machine_scale_set": resourceArmVirtualMachineScaleSet(), + "azurerm_virtual_network": resourceArmVirtualNetwork(), + "azurerm_virtual_network_gateway": resourceArmVirtualNetworkGateway(), + "azurerm_virtual_network_gateway_connection": resourceArmVirtualNetworkGatewayConnection(), + "azurerm_virtual_network_peering": resourceArmVirtualNetworkPeering(), }, } @@ -201,11 +215,27 @@ func providerConfigure(p *schema.Provider) schema.ConfigureFunc { ClientSecret: d.Get("client_secret").(string), TenantID: d.Get("tenant_id").(string), Environment: d.Get("environment").(string), + UseMsi: d.Get("use_msi").(bool), + MsiEndpoint: d.Get("msi_endpoint").(string), SkipCredentialsValidation: d.Get("skip_credentials_validation").(bool), SkipProviderRegistration: d.Get("skip_provider_registration").(bool), } - if config.ClientSecret != "" { + if config.UseMsi { + log.Printf("[DEBUG] use_msi specified - using MSI Authentication") + if config.MsiEndpoint == "" { + msiEndpoint, err := adal.GetMSIVMEndpoint() + if err != nil { + return nil, fmt.Errorf("Could not retrieve MSI endpoint from VM settings."+ + "Ensure the VM has MSI enabled, or try setting msi_endpoint. Error: %s", err) + } + config.MsiEndpoint = msiEndpoint + } + log.Printf("[DEBUG] Using MSI endpoint %s", config.MsiEndpoint) + if err := config.ValidateMsi(); err != nil { + return nil, err + } + } else if config.ClientSecret != "" { log.Printf("[DEBUG] Client Secret specified - using Service Principal for Authentication") if err := config.ValidateServicePrincipal(); err != nil { return nil, err diff --git a/azurerm/resource_arm_application_security_group.go b/azurerm/resource_arm_application_security_group.go new file mode 100644 index 000000000000..ce3b8bf3352b --- /dev/null +++ b/azurerm/resource_arm_application_security_group.go @@ -0,0 +1,132 @@ +package azurerm + +import ( + "fmt" + "log" + + "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2017-09-01/network" + "github.com/hashicorp/terraform/helper/schema" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/response" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func resourceArmApplicationSecurityGroup() *schema.Resource { + return &schema.Resource{ + Create: resourceArmApplicationSecurityGroupCreateUpdate, + Read: resourceArmApplicationSecurityGroupRead, + Update: resourceArmApplicationSecurityGroupCreateUpdate, + Delete: resourceArmApplicationSecurityGroupDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "location": locationSchema(), + + "resource_group_name": resourceGroupNameSchema(), + + "tags": tagsSchema(), + }, + } +} + +func resourceArmApplicationSecurityGroupCreateUpdate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).applicationSecurityGroupsClient + ctx := meta.(*ArmClient).StopContext + + resourceGroup := d.Get("resource_group_name").(string) + name := d.Get("name").(string) + location := d.Get("location").(string) + tags := d.Get("tags").(map[string]interface{}) + + securityGroup := network.ApplicationSecurityGroup{ + Location: utils.String(location), + Tags: expandTags(tags), + } + future, err := client.CreateOrUpdate(ctx, resourceGroup, name, securityGroup) + if err != nil { + return fmt.Errorf("Error creating Application Security Group %q (Resource Group %q): %+v", name, resourceGroup, err) + } + + err = future.WaitForCompletion(ctx, client.Client) + if err != nil { + return fmt.Errorf("Error waiting for the Application Security Group %q (Resource Group %q) to finish creating: %+v", name, resourceGroup, err) + } + + read, err := client.Get(ctx, resourceGroup, name) + if err != nil { + return err + } + if read.ID == nil { + return fmt.Errorf("Cannot read Application Security Group %q (Resource Group %q) ID", name, resourceGroup) + } + + d.SetId(*read.ID) + + return resourceArmApplicationSecurityGroupRead(d, meta) +} + +func resourceArmApplicationSecurityGroupRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).applicationSecurityGroupsClient + ctx := meta.(*ArmClient).StopContext + + id, err := parseAzureResourceID(d.Id()) + if err != nil { + return err + } + resourceGroup := id.ResourceGroup + name := id.Path["applicationSecurityGroups"] + + resp, err := client.Get(ctx, resourceGroup, name) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + d.SetId("") + return nil + } + + return fmt.Errorf("Error making Read request on Application Security Group %q (Resource Group %q): %+v", name, resourceGroup, err) + } + + d.Set("name", resp.Name) + d.Set("location", azureRMNormalizeLocation(*resp.Location)) + d.Set("resource_group_name", resourceGroup) + flattenAndSetTags(d, resp.Tags) + + return nil +} + +func resourceArmApplicationSecurityGroupDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).applicationSecurityGroupsClient + ctx := meta.(*ArmClient).StopContext + + id, err := parseAzureResourceID(d.Id()) + if err != nil { + return err + } + resourceGroup := id.ResourceGroup + name := id.Path["applicationSecurityGroups"] + + log.Printf("[DEBUG] Deleting Application Security Group %q (resource group %q)", name, resourceGroup) + + future, err := client.Delete(ctx, resourceGroup, name) + if err != nil { + if !response.WasNotFound(future.Response()) { + return fmt.Errorf("Error issuing delete request for Application Security Group %q (Resource Group %q): %+v", name, resourceGroup, err) + } + } + + err = future.WaitForCompletion(ctx, client.Client) + if err != nil { + if !response.WasNotFound(future.Response()) { + return fmt.Errorf("Error waiting for deletion of Application Security Group %q (Resource Group %q): %+v", name, resourceGroup, err) + } + } + + return nil +} diff --git a/azurerm/resource_arm_application_security_group_test.go b/azurerm/resource_arm_application_security_group_test.go new file mode 100644 index 000000000000..a4387826f730 --- /dev/null +++ b/azurerm/resource_arm_application_security_group_test.go @@ -0,0 +1,174 @@ +package azurerm + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func TestAccAzureRMApplicationSecurityGroup_basic(t *testing.T) { + ri := acctest.RandInt() + resourceName := "azurerm_application_security_group.test" + config := testAccAzureRMApplicationSecurityGroup_basic(ri, testLocation()) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMApplicationSecurityGroupDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMApplicationSecurityGroupExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + ), + }, + }, + }) +} + +func TestAccAzureRMApplicationSecurityGroup_complete(t *testing.T) { + ri := acctest.RandInt() + resourceName := "azurerm_application_security_group.test" + config := testAccAzureRMApplicationSecurityGroup_complete(ri, testLocation()) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMApplicationSecurityGroupDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMApplicationSecurityGroupExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.Hello", "World"), + ), + }, + }, + }) +} + +func TestAccAzureRMApplicationSecurityGroup_update(t *testing.T) { + ri := acctest.RandInt() + location := testLocation() + resourceName := "azurerm_application_security_group.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMApplicationSecurityGroupDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMApplicationSecurityGroup_basic(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMApplicationSecurityGroupExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + ), + }, + { + Config: testAccAzureRMApplicationSecurityGroup_complete(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMApplicationSecurityGroupExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.Hello", "World"), + ), + }, + }, + }) +} + +func testCheckAzureRMApplicationSecurityGroupDestroy(s *terraform.State) error { + for _, rs := range s.RootModule().Resources { + if rs.Type != "azurerm_application_security_group" { + continue + } + + name := rs.Primary.Attributes["name"] + resourceGroup := rs.Primary.Attributes["resource_group_name"] + + client := testAccProvider.Meta().(*ArmClient).applicationSecurityGroupsClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + resp, err := client.Get(ctx, resourceGroup, name) + + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + return nil + } + + return err + } + + return fmt.Errorf("Applicaton Security Group still exists:\n%#v", resp) + } + + return nil +} + +func testCheckAzureRMApplicationSecurityGroupExists(name string) resource.TestCheckFunc { + return func(s *terraform.State) error { + // Ensure we have enough information in state to look up in API + rs, ok := s.RootModule().Resources[name] + if !ok { + return fmt.Errorf("Not found: %q", name) + } + + name := rs.Primary.Attributes["name"] + resourceGroup, hasResourceGroup := rs.Primary.Attributes["resource_group_name"] + if !hasResourceGroup { + return fmt.Errorf("Bad: no resource group found in state for Application Security Group: %q", name) + } + + client := testAccProvider.Meta().(*ArmClient).applicationSecurityGroupsClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + resp, err := client.Get(ctx, resourceGroup, name) + + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + return fmt.Errorf("Application Security Group %q (resource group: %q) was not found: %+v", name, resourceGroup, err) + } + + return fmt.Errorf("Bad: Get on applicationSecurityGroupsClient: %+v", err) + } + + return nil + } +} + +func testAccAzureRMApplicationSecurityGroup_basic(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_application_security_group" "test" { + name = "acctest-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" +} +`, rInt, location, rInt) +} + +func testAccAzureRMApplicationSecurityGroup_complete(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_application_security_group" "test" { + name = "acctest-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + tags { + "Hello" = "World" + } +} +`, rInt, location, rInt) +} diff --git a/azurerm/resource_arm_kubernetes_cluster.go b/azurerm/resource_arm_kubernetes_cluster.go index dea24930099c..fbbe848b8aec 100644 --- a/azurerm/resource_arm_kubernetes_cluster.go +++ b/azurerm/resource_arm_kubernetes_cluster.go @@ -54,10 +54,13 @@ func resourceArmKubernetesCluster() *schema.Resource { "admin_username": { Type: schema.TypeString, Required: true, + ForceNew: true, }, "ssh_key": { Type: schema.TypeList, Required: true, + ForceNew: true, + Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "key_data": { @@ -376,9 +379,10 @@ func expandAzureRmKubernetesClusterLinuxProfile(d *schema.ResourceData) containe adminUsername := config["admin_username"].(string) linuxKeys := config["ssh_key"].([]interface{}) - key := linuxKeys[0].(map[string]interface{}) - keyData := key["key_data"].(string) - + keyData := "" + if key, ok := linuxKeys[0].(map[string]interface{}); ok { + keyData = key["key_data"].(string) + } sshPublicKey := containerservice.SSHPublicKey{ KeyData: &keyData, } diff --git a/azurerm/resource_arm_network_interface.go b/azurerm/resource_arm_network_interface.go index ddc8f44fadfe..7a65ea890d67 100644 --- a/azurerm/resource_arm_network_interface.go +++ b/azurerm/resource_arm_network_interface.go @@ -104,6 +104,14 @@ func resourceArmNetworkInterface() *schema.Resource { Set: schema.HashString, }, + "application_security_group_ids": { + Type: schema.TypeSet, + Optional: true, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + Set: schema.HashString, + }, + "primary": { Type: schema.TypeBool, Optional: true, @@ -329,7 +337,10 @@ func resourceArmNetworkInterfaceRead(d *schema.ResourceData, meta interface{}) e } if iface.IPConfigurations != nil { - d.Set("ip_configuration", flattenNetworkInterfaceIPConfigurations(iface.IPConfigurations)) + configs := flattenNetworkInterfaceIPConfigurations(iface.IPConfigurations) + if err := d.Set("ip_configuration", configs); err != nil { + return fmt.Errorf("Error setting `ip_configuration`: %+v", err) + } } if iface.VirtualMachine != nil { @@ -480,6 +491,14 @@ func flattenNetworkInterfaceIPConfigurations(ipConfigs *[]network.InterfaceIPCon } niIPConfig["load_balancer_inbound_nat_rules_ids"] = schema.NewSet(schema.HashString, rules) + securityGroups := make([]interface{}, 0) + if sgs := props.ApplicationSecurityGroups; sgs != nil { + for _, sg := range *sgs { + securityGroups = append(securityGroups, *sg.ID) + } + } + niIPConfig["application_security_group_ids"] = schema.NewSet(schema.HashString, securityGroups) + result = append(result, niIPConfig) } return result @@ -566,6 +585,21 @@ func expandAzureRmNetworkInterfaceIpConfigurations(d *schema.ResourceData) ([]ne properties.LoadBalancerInboundNatRules = &natRules } + if v, ok := data["application_security_group_ids"]; ok { + var securityGroups []network.ApplicationSecurityGroup + rules := v.(*schema.Set).List() + for _, r := range rules { + groupId := r.(string) + group := network.ApplicationSecurityGroup{ + ID: &groupId, + } + + securityGroups = append(securityGroups, group) + } + + properties.ApplicationSecurityGroups = &securityGroups + } + name := data["name"].(string) ipConfig := network.InterfaceIPConfiguration{ Name: &name, diff --git a/azurerm/resource_arm_network_interface_test.go b/azurerm/resource_arm_network_interface_test.go index 1caea1568e23..0ad116ee06fb 100644 --- a/azurerm/resource_arm_network_interface_test.go +++ b/azurerm/resource_arm_network_interface_test.go @@ -332,6 +332,25 @@ func TestAccAzureRMNetworkInterface_bug7986(t *testing.T) { }) } +func TestAccAzureRMNetworkInterface_applicationSecurityGroups(t *testing.T) { + resourceName := "azurerm_network_interface.test" + rInt := acctest.RandInt() + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMNetworkInterfaceDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMNetworkInterface_applicationSecurityGroup(rInt, testLocation()), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMNetworkInterfaceExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "ip_configuration.0.application_security_group_ids.#", "1"), + ), + }, + }, + }) +} + func testCheckAzureRMNetworkInterfaceExists(name string) resource.TestCheckFunc { return func(s *terraform.State) error { // Ensure we have enough information in state to look up in API @@ -1012,3 +1031,45 @@ resource "azurerm_network_interface" "test" { `, rInt, location, rInt, rInt, rInt) } + +func testAccAzureRMNetworkInterface_applicationSecurityGroup(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctest-rg-%d" + location = "%s" +} + +resource "azurerm_virtual_network" "test" { + name = "acctestvn-%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 = "testsubnet" + 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_application_security_group" "test" { + name = "acctest-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" +} + +resource "azurerm_network_interface" "test" { + name = "acctestnic-%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" + application_security_group_ids = ["${azurerm_application_security_group.test.id}"] + } +} +`, rInt, location, rInt, rInt, rInt) +} diff --git a/azurerm/resource_arm_network_security_group.go b/azurerm/resource_arm_network_security_group.go index ae7545c24a93..8b494cc35856 100644 --- a/azurerm/resource_arm_network_security_group.go +++ b/azurerm/resource_arm_network_security_group.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2017-09-01/network" + multierror "github.com/hashicorp/go-multierror" "github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/validation" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" @@ -50,30 +51,62 @@ func resourceArmNetworkSecurityGroup() *schema.Resource { }, "protocol": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validateNetworkSecurityRuleProtocol, - StateFunc: ignoreCaseStateFunc, + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + string(network.SecurityRuleProtocolAsterisk), + string(network.SecurityRuleProtocolTCP), + string(network.SecurityRuleProtocolUDP), + }, true), + DiffSuppressFunc: ignoreCaseDiffSuppressFunc, }, "source_port_range": { Type: schema.TypeString, - Required: true, + Optional: true, + }, + + "source_port_ranges": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + Set: schema.HashString, }, "destination_port_range": { Type: schema.TypeString, - Required: true, + Optional: true, + }, + + "destination_port_ranges": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + Set: schema.HashString, }, "source_address_prefix": { Type: schema.TypeString, - Required: true, + Optional: true, + }, + + "source_address_prefixes": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + Set: schema.HashString, }, "destination_address_prefix": { Type: schema.TypeString, - Required: true, + Optional: true, + }, + + "destination_address_prefixes": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + Set: schema.HashString, }, "access": { @@ -184,7 +217,10 @@ func resourceArmNetworkSecurityGroupRead(d *schema.ResourceData, meta interface{ d.Set("location", azureRMNormalizeLocation(*resp.Location)) if props := resp.SecurityGroupPropertiesFormat; props != nil { - d.Set("security_rule", flattenNetworkSecurityRules(props.SecurityRules)) + flattenedRules := flattenNetworkSecurityRules(props.SecurityRules) + if err := d.Set("security_rule", flattenedRules); err != nil { + return fmt.Errorf("Error flattening `security_rule`: %+v", err) + } } flattenAndSetTags(d, resp.Tags) @@ -216,6 +252,87 @@ func resourceArmNetworkSecurityGroupDelete(d *schema.ResourceData, meta interfac return err } +func expandAzureRmSecurityRules(d *schema.ResourceData) ([]network.SecurityRule, error) { + sgRules := d.Get("security_rule").(*schema.Set).List() + rules := make([]network.SecurityRule, 0) + + for _, sgRaw := range sgRules { + sgRule := sgRaw.(map[string]interface{}) + + if err := validateSecurityRule(sgRule); err != nil { + return nil, err + } + + name := sgRule["name"].(string) + source_port_range := sgRule["source_port_range"].(string) + destination_port_range := sgRule["destination_port_range"].(string) + source_address_prefix := sgRule["source_address_prefix"].(string) + destination_address_prefix := sgRule["destination_address_prefix"].(string) + priority := int32(sgRule["priority"].(int)) + access := sgRule["access"].(string) + direction := sgRule["direction"].(string) + protocol := sgRule["protocol"].(string) + + properties := network.SecurityRulePropertiesFormat{ + SourcePortRange: &source_port_range, + DestinationPortRange: &destination_port_range, + SourceAddressPrefix: &source_address_prefix, + DestinationAddressPrefix: &destination_address_prefix, + Priority: &priority, + Access: network.SecurityRuleAccess(access), + Direction: network.SecurityRuleDirection(direction), + Protocol: network.SecurityRuleProtocol(protocol), + } + + if v := sgRule["description"].(string); v != "" { + properties.Description = &v + } + + if r, ok := sgRule["source_port_ranges"].(*schema.Set); ok && r.Len() > 0 { + var sourcePortRanges []string + for _, v := range r.List() { + s := v.(string) + sourcePortRanges = append(sourcePortRanges, s) + } + properties.SourcePortRanges = &sourcePortRanges + } + + if r, ok := sgRule["destination_port_ranges"].(*schema.Set); ok && r.Len() > 0 { + var destinationPortRanges []string + for _, v := range r.List() { + s := v.(string) + destinationPortRanges = append(destinationPortRanges, s) + } + properties.DestinationPortRanges = &destinationPortRanges + } + + if r, ok := sgRule["source_address_prefixes"].(*schema.Set); ok && r.Len() > 0 { + var sourceAddressPrefixes []string + for _, v := range r.List() { + s := v.(string) + sourceAddressPrefixes = append(sourceAddressPrefixes, s) + } + properties.SourceAddressPrefixes = &sourceAddressPrefixes + } + + if r, ok := sgRule["destination_address_prefixes"].(*schema.Set); ok && r.Len() > 0 { + var destinationAddressPrefixes []string + for _, v := range r.List() { + s := v.(string) + destinationAddressPrefixes = append(destinationAddressPrefixes, s) + } + properties.DestinationAddressPrefixes = &destinationAddressPrefixes + } + + rules = append(rules, network.SecurityRule{ + Name: &name, + SecurityRulePropertiesFormat: &properties, + }) + } + + return rules, nil +} + func flattenNetworkSecurityRules(rules *[]network.SecurityRule) []map[string]interface{} { result := make([]map[string]interface{}, 0) @@ -225,26 +342,39 @@ func flattenNetworkSecurityRules(rules *[]network.SecurityRule) []map[string]int sgRule["name"] = *rule.Name if props := rule.SecurityRulePropertiesFormat; props != nil { + if props.Description != nil { + sgRule["description"] = *props.Description + } + if props.DestinationAddressPrefix != nil { sgRule["destination_address_prefix"] = *props.DestinationAddressPrefix } + if props.DestinationAddressPrefixes != nil { + sgRule["destination_address_prefixes"] = sliceToSet(*props.DestinationAddressPrefixes) + } if props.DestinationPortRange != nil { sgRule["destination_port_range"] = *props.DestinationPortRange } + if props.DestinationPortRanges != nil { + sgRule["destination_port_ranges"] = sliceToSet(*props.DestinationPortRanges) + } if props.SourceAddressPrefix != nil { sgRule["source_address_prefix"] = *props.SourceAddressPrefix } + if props.SourceAddressPrefixes != nil { + sgRule["source_address_prefixes"] = sliceToSet(*props.SourceAddressPrefixes) + } if props.SourcePortRange != nil { sgRule["source_port_range"] = *props.SourcePortRange } + if props.SourcePortRanges != nil { + sgRule["source_port_ranges"] = sliceToSet(*props.SourcePortRanges) + } + + sgRule["protocol"] = string(props.Protocol) sgRule["priority"] = int(*props.Priority) sgRule["access"] = string(props.Access) sgRule["direction"] = string(props.Direction) - sgRule["protocol"] = string(props.Protocol) - - if props.Description != nil { - sgRule["description"] = *props.Description - } } result = append(result, sgRule) @@ -254,45 +384,42 @@ func flattenNetworkSecurityRules(rules *[]network.SecurityRule) []map[string]int return result } -func expandAzureRmSecurityRules(d *schema.ResourceData) ([]network.SecurityRule, error) { - sgRules := d.Get("security_rule").(*schema.Set).List() - rules := make([]network.SecurityRule, 0, len(sgRules)) - - for _, sgRaw := range sgRules { - data := sgRaw.(map[string]interface{}) - - name := data["name"].(string) - source_port_range := data["source_port_range"].(string) - destination_port_range := data["destination_port_range"].(string) - source_address_prefix := data["source_address_prefix"].(string) - destination_address_prefix := data["destination_address_prefix"].(string) - priority := int32(data["priority"].(int)) - access := data["access"].(string) - direction := data["direction"].(string) - protocol := data["protocol"].(string) - - properties := network.SecurityRulePropertiesFormat{ - SourcePortRange: &source_port_range, - DestinationPortRange: &destination_port_range, - SourceAddressPrefix: &source_address_prefix, - DestinationAddressPrefix: &destination_address_prefix, - Priority: &priority, - Access: network.SecurityRuleAccess(access), - Direction: network.SecurityRuleDirection(direction), - Protocol: network.SecurityRuleProtocol(protocol), - } - - if v := data["description"].(string); v != "" { - properties.Description = &v - } - - rule := network.SecurityRule{ - Name: &name, - SecurityRulePropertiesFormat: &properties, - } +func sliceToSet(slice []string) *schema.Set { + set := &schema.Set{F: schema.HashString} + for _, v := range slice { + set.Add(v) + } + return set +} - rules = append(rules, rule) +func validateSecurityRule(sgRule map[string]interface{}) error { + var err *multierror.Error + + sourcePortRange := sgRule["source_port_range"].(string) + sourcePortRanges := sgRule["source_port_ranges"].(*schema.Set) + destinationPortRange := sgRule["destination_port_range"].(string) + destinationPortRanges := sgRule["destination_port_ranges"].(*schema.Set) + sourceAddressPrefix := sgRule["source_address_prefix"].(string) + sourceAddressPrefixes := sgRule["source_address_prefixes"].(*schema.Set) + destinationAddressPrefix := sgRule["destination_address_prefix"].(string) + destinationAddressPrefixes := sgRule["destination_address_prefixes"].(*schema.Set) + + if sourcePortRange != "" && sourcePortRanges.Len() > 0 { + err = multierror.Append(err, fmt.Errorf( + "only one of \"source_port_range\" and \"source_port_ranges\" can be used per security rule")) + } + if destinationPortRange != "" && destinationPortRanges.Len() > 0 { + err = multierror.Append(err, fmt.Errorf( + "only one of \"destination_port_range\" and \"destination_port_ranges\" can be used per security rule")) + } + if sourceAddressPrefix != "" && sourceAddressPrefixes.Len() > 0 { + err = multierror.Append(err, fmt.Errorf( + "only one of \"source_address_prefix\" and \"source_address_prefixes\" can be used per security rule")) + } + if destinationAddressPrefix != "" && destinationAddressPrefixes.Len() > 0 { + err = multierror.Append(err, fmt.Errorf( + "only one of \"destination_address_prefix\" and \"destination_address_prefixes\" can be used per security rule")) } - return rules, nil + return err.ErrorOrNil() } diff --git a/azurerm/resource_arm_network_security_group_test.go b/azurerm/resource_arm_network_security_group_test.go index 4d40b31990c0..e473b0ed9415 100644 --- a/azurerm/resource_arm_network_security_group_test.go +++ b/azurerm/resource_arm_network_security_group_test.go @@ -149,6 +149,25 @@ func TestAccAzureRMNetworkSecurityGroup_addingExtraRules(t *testing.T) { }) } +func TestAccAzureRMNetworkSecurityGroup_augmented(t *testing.T) { + resourceName := "azurerm_network_security_group.test" + rInt := acctest.RandInt() + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMNetworkSecurityGroupDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMNetworkSecurityGroup_augmented(rInt, testLocation()), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMNetworkSecurityGroupExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "security_rule.#", "1"), + ), + }, + }, + }) +} + func testCheckAzureRMNetworkSecurityGroupExists(name string) resource.TestCheckFunc { return func(s *terraform.State) error { @@ -377,3 +396,30 @@ resource "azurerm_network_security_group" "test" { `, rInt, location) } + +func testAccAzureRMNetworkSecurityGroup_augmented(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_network_security_group" "test" { + name = "acceptanceTestSecurityGroup1" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + + security_rule { + name = "test123" + priority = 100 + direction = "Inbound" + access = "Allow" + protocol = "Tcp" + source_port_ranges = [ "10000-40000" ] + destination_port_ranges = [ "80", "443", "8080", "8190" ] + source_address_prefixes = [ "10.0.0.0/8", "192.168.0.0/16" ] + destination_address_prefixes = [ "172.16.0.0/20", "8.8.8.8" ] + } +} +`, rInt, location) +} diff --git a/azurerm/resource_arm_network_security_rule.go b/azurerm/resource_arm_network_security_rule.go index df4f42fe49b4..71af8b0aee02 100644 --- a/azurerm/resource_arm_network_security_rule.go +++ b/azurerm/resource_arm_network_security_rule.go @@ -263,19 +263,19 @@ func resourceArmNetworkSecurityRuleRead(d *schema.ResourceData, meta interface{} d.Set("resource_group_name", resGroup) if props := resp.SecurityRulePropertiesFormat; props != nil { - d.Set("access", string(props.Access)) - d.Set("destination_address_prefix", props.DestinationAddressPrefix) - d.Set("destination_port_range", props.DestinationPortRange) - d.Set("direction", string(props.Direction)) d.Set("description", props.Description) - d.Set("priority", int(*props.Priority)) d.Set("protocol", string(props.Protocol)) + d.Set("destination_address_prefix", props.DestinationAddressPrefix) + d.Set("destination_address_prefixes", props.DestinationAddressPrefixes) + d.Set("destination_port_range", props.DestinationPortRange) + d.Set("destination_port_ranges", props.DestinationPortRanges) d.Set("source_address_prefix", props.SourceAddressPrefix) - d.Set("source_port_range", props.SourcePortRange) d.Set("source_address_prefixes", props.SourceAddressPrefixes) - d.Set("destination_address_prefixes", props.DestinationAddressPrefixes) + d.Set("source_port_range", props.SourcePortRange) d.Set("source_port_ranges", props.SourcePortRanges) - d.Set("destination_port_ranges", props.DestinationPortRanges) + d.Set("access", string(props.Access)) + d.Set("priority", int(*props.Priority)) + d.Set("direction", string(props.Direction)) } return nil diff --git a/azurerm/resource_arm_servicebus_topic_authorization_rule.go b/azurerm/resource_arm_servicebus_topic_authorization_rule.go new file mode 100644 index 000000000000..422c8c2a838c --- /dev/null +++ b/azurerm/resource_arm_servicebus_topic_authorization_rule.go @@ -0,0 +1,239 @@ +package azurerm + +import ( + "fmt" + "log" + + "github.com/Azure/azure-sdk-for-go/services/servicebus/mgmt/2017-04-01/servicebus" + "github.com/hashicorp/terraform/helper/schema" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func resourceArmServiceBusTopicAuthorizationRule() *schema.Resource { + return &schema.Resource{ + Create: resourceArmServiceBusTopicAuthorizationRuleCreateUpdate, + Read: resourceArmServiceBusTopicAuthorizationRuleRead, + Update: resourceArmServiceBusTopicAuthorizationRuleCreateUpdate, + Delete: resourceArmServiceBusTopicAuthorizationRuleDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "namespace_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "topic_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "resource_group_name": resourceGroupNameSchema(), + + "listen": { + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + + "send": { + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + + "manage": { + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + + "primary_key": { + Type: schema.TypeString, + Computed: true, + }, + + "primary_connection_string": { + Type: schema.TypeString, + Computed: true, + }, + + "secondary_key": { + Type: schema.TypeString, + Computed: true, + }, + + "secondary_connection_string": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func resourceArmServiceBusTopicAuthorizationRuleCreateUpdate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).serviceBusTopicsClient + ctx := meta.(*ArmClient).StopContext + log.Printf("[INFO] preparing arguments for AzureRM ServiceBus Topic Authorization Rule creation.") + + name := d.Get("name").(string) + namespaceName := d.Get("namespace_name").(string) + topicName := d.Get("topic_name").(string) + resGroup := d.Get("resource_group_name").(string) + + rights, err := expandServiceBusTopicAuthorizationRuleAccessRights(d) + if err != nil { + return err + } + + parameters := servicebus.SBAuthorizationRule{ + Name: &name, + SBAuthorizationRuleProperties: &servicebus.SBAuthorizationRuleProperties{ + Rights: rights, + }, + } + + _, err = client.CreateOrUpdateAuthorizationRule(ctx, resGroup, namespaceName, topicName, name, parameters) + if err != nil { + return err + } + + read, err := client.GetAuthorizationRule(ctx, resGroup, namespaceName, topicName, name) + if err != nil { + return err + } + + if read.ID == nil { + return fmt.Errorf("Cannot read ServiceBus Topic Authorization Rule %s (resource group %s) ID", name, resGroup) + } + + d.SetId(*read.ID) + + return resourceArmServiceBusTopicAuthorizationRuleRead(d, meta) +} + +func resourceArmServiceBusTopicAuthorizationRuleRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).serviceBusTopicsClient + ctx := meta.(*ArmClient).StopContext + + id, err := parseAzureResourceID(d.Id()) + if err != nil { + return err + } + resGroup := id.ResourceGroup + namespaceName := id.Path["namespaces"] + topicsName := id.Path["topics"] + name := id.Path["authorizationRules"] + + resp, err := client.GetAuthorizationRule(ctx, resGroup, namespaceName, topicsName, name) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + d.SetId("") + return nil + } + return fmt.Errorf("Error making Read request on Azure ServiceBus Topic Authorization Rule %s: %+v", name, err) + } + + keysResp, err := client.ListKeys(ctx, resGroup, namespaceName, topicsName, name) + if err != nil { + return fmt.Errorf("Error making Read request on Azure ServiceBus Topic Authorization Rule List Keys %s: %+v", name, err) + } + + d.Set("name", name) + d.Set("topic_name", topicsName) + d.Set("namespace_name", namespaceName) + d.Set("resource_group_name", resGroup) + + flattenServiceBusTopicAuthorizationRuleAccessRights(d, resp) + + d.Set("primary_key", keysResp.PrimaryKey) + d.Set("primary_connection_string", keysResp.PrimaryConnectionString) + d.Set("secondary_key", keysResp.SecondaryKey) + d.Set("secondary_connection_string", keysResp.SecondaryConnectionString) + + return nil +} + +func resourceArmServiceBusTopicAuthorizationRuleDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).serviceBusTopicsClient + ctx := meta.(*ArmClient).StopContext + + id, err := parseAzureResourceID(d.Id()) + if err != nil { + return err + } + resGroup := id.ResourceGroup + namespaceName := id.Path["namespaces"] + topicName := id.Path["topics"] + name := id.Path["authorizationRules"] + + _, err = client.DeleteAuthorizationRule(ctx, resGroup, namespaceName, topicName, name) + + if err != nil { + return fmt.Errorf("Error issuing Azure ARM delete request of ServiceBus Topic Authorization Rule %q (Resource Group %q): %+v", name, resGroup, err) + } + + return nil +} + +func expandServiceBusTopicAuthorizationRuleAccessRights(d *schema.ResourceData) (*[]servicebus.AccessRights, error) { + canSend := d.Get("send").(bool) + canListen := d.Get("listen").(bool) + canManage := d.Get("manage").(bool) + rights := []servicebus.AccessRights{} + if canListen { + rights = append(rights, servicebus.Listen) + } + + if canSend { + rights = append(rights, servicebus.Send) + } + + if canManage { + rights = append(rights, servicebus.Manage) + } + + if len(rights) == 0 { + return nil, fmt.Errorf("At least one Authorization Rule State must be enabled (e.g. Listen/Manage/Send)") + } + + if canManage && !(canListen && canSend) { + return nil, fmt.Errorf("In order to enable the 'Manage' Authorization Rule - both the 'Listen' and 'Send' rules must be enabled") + } + + return &rights, nil +} + +func flattenServiceBusTopicAuthorizationRuleAccessRights(d *schema.ResourceData, resp servicebus.SBAuthorizationRule) { + + var canListen = false + var canSend = false + var canManage = false + + for _, right := range *resp.Rights { + switch right { + case servicebus.Listen: + canListen = true + case servicebus.Send: + canSend = true + case servicebus.Manage: + canManage = true + default: + log.Printf("[DEBUG] Unknown Authorization Rule Right '%s'", right) + } + } + + d.Set("listen", canListen) + d.Set("send", canSend) + d.Set("manage", canManage) +} diff --git a/azurerm/resource_arm_servicebus_topic_authorization_rule_test.go b/azurerm/resource_arm_servicebus_topic_authorization_rule_test.go new file mode 100644 index 000000000000..464731d5a30e --- /dev/null +++ b/azurerm/resource_arm_servicebus_topic_authorization_rule_test.go @@ -0,0 +1,271 @@ +package azurerm + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func TestAccAzureRMServiceBusTopicAuthorizationRule_listen(t *testing.T) { + ri := acctest.RandInt() + config := testAccAzureRMServiceBusTopicAuthorizationRule_listen(ri, testLocation()) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMServiceBusTopicAuthorizationRuleDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMServiceBusTopicAuthorizationRuleExists("azurerm_servicebus_topic_authorization_rule.test"), + ), + }, + }, + }) +} + +func TestAccAzureRMServiceBusTopicAuthorizationRule_send(t *testing.T) { + ri := acctest.RandInt() + config := testAccAzureRMServiceBusTopicAuthorizationRule_send(ri, testLocation()) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMServiceBusTopicAuthorizationRuleDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMServiceBusTopicAuthorizationRuleExists("azurerm_servicebus_topic_authorization_rule.test"), + ), + }, + }, + }) +} + +func TestAccAzureRMServiceBusTopicAuthorizationRule_readwrite(t *testing.T) { + ri := acctest.RandInt() + config := testAccAzureRMServiceBusTopicAuthorizationRule_readWrite(ri, testLocation()) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMServiceBusTopicAuthorizationRuleDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMServiceBusTopicAuthorizationRuleExists("azurerm_servicebus_topic_authorization_rule.test"), + ), + }, + }, + }) +} + +func TestAccAzureRMServiceBusTopicAuthorizationRule_manage(t *testing.T) { + ri := acctest.RandInt() + config := testAccAzureRMServiceBusTopicAuthorizationRule_manage(ri, testLocation()) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMServiceBusTopicAuthorizationRuleDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMServiceBusTopicAuthorizationRuleExists("azurerm_servicebus_topic_authorization_rule.test"), + ), + }, + }, + }) +} + +func testCheckAzureRMServiceBusTopicAuthorizationRuleDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*ArmClient).serviceBusTopicsClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + for _, rs := range s.RootModule().Resources { + if rs.Type != "azurerm_servicebus_topic_authorization_rule" { + continue + } + + name := rs.Primary.Attributes["name"] + namespaceName := rs.Primary.Attributes["namespace_name"] + topicName := rs.Primary.Attributes["topic_name"] + resourceGroup := rs.Primary.Attributes["resource_group_name"] + + resp, err := conn.GetAuthorizationRule(ctx, resourceGroup, namespaceName, topicName, name) + if err != nil { + if !utils.ResponseWasNotFound(resp.Response) { + return err + } + } + } + + return nil +} + +func testCheckAzureRMServiceBusTopicAuthorizationRuleExists(name string) resource.TestCheckFunc { + return func(s *terraform.State) error { + // Ensure we have enough information in state to look up in API + rs, ok := s.RootModule().Resources[name] + if !ok { + return fmt.Errorf("Not found: %s", name) + } + + name := rs.Primary.Attributes["name"] + namespaceName := rs.Primary.Attributes["namespace_name"] + topicName := rs.Primary.Attributes["topic_name"] + resourceGroup, hasResourceGroup := rs.Primary.Attributes["resource_group_name"] + if !hasResourceGroup { + return fmt.Errorf("Bad: no resource group found in state for ServiceBus Topic: %s", name) + } + + conn := testAccProvider.Meta().(*ArmClient).serviceBusTopicsClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + resp, err := conn.GetAuthorizationRule(ctx, resourceGroup, namespaceName, topicName, name) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + return fmt.Errorf("Bad: ServiceBus Topic Authorization Rule %q (topic %s, namespace %s / resource group: %s) does not exist", name, topicName, namespaceName, resourceGroup) + } + + return fmt.Errorf("Bad: Get on serviceBusTopicsClient: %+v", err) + } + + return nil + } +} + +func testAccAzureRMServiceBusTopicAuthorizationRule_listen(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_servicebus_namespace" "test" { + name = "acctestservicebusnamespace-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + sku = "Standard" +} + +resource "azurerm_servicebus_topic" "test" { + name = "acctestservicebustopic-%d" + namespace_name = "${azurerm_servicebus_namespace.test.name}" + resource_group_name = "${azurerm_resource_group.test.name}" +} + +resource "azurerm_servicebus_topic_authorization_rule" "test" { + name = "acctestservicebustopicrule-%d" + namespace_name = "${azurerm_servicebus_namespace.test.name}" + topic_name = "${azurerm_servicebus_topic.test.name}" + resource_group_name = "${azurerm_resource_group.test.name}" + listen = true + send = false + manage = false +} +`, rInt, location, rInt, rInt, rInt) +} + +func testAccAzureRMServiceBusTopicAuthorizationRule_send(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_servicebus_namespace" "test" { + name = "acctestservicebusnamespace-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + sku = "Standard" +} + +resource "azurerm_servicebus_topic" "test" { + name = "acctestservicebustopic-%d" + namespace_name = "${azurerm_servicebus_namespace.test.name}" + resource_group_name = "${azurerm_resource_group.test.name}" +} + +resource "azurerm_servicebus_topic_authorization_rule" "test" { + name = "acctestservicebustopicrule-%d" + namespace_name = "${azurerm_servicebus_namespace.test.name}" + topic_name = "${azurerm_servicebus_topic.test.name}" + resource_group_name = "${azurerm_resource_group.test.name}" + listen = false + send = true + manage = false +} +`, rInt, location, rInt, rInt, rInt) +} + +func testAccAzureRMServiceBusTopicAuthorizationRule_readWrite(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_servicebus_namespace" "test" { + name = "acctestservicebusnamespace-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + sku = "Standard" +} + +resource "azurerm_servicebus_topic" "test" { + name = "acctestservicebustopic-%d" + namespace_name = "${azurerm_servicebus_namespace.test.name}" + resource_group_name = "${azurerm_resource_group.test.name}" +} + +resource "azurerm_servicebus_topic_authorization_rule" "test" { + name = "acctestservicebustopicrule-%d" + namespace_name = "${azurerm_servicebus_namespace.test.name}" + topic_name = "${azurerm_servicebus_topic.test.name}" + resource_group_name = "${azurerm_resource_group.test.name}" + listen = true + send = true + manage = false +} +`, rInt, location, rInt, rInt, rInt) +} + +func testAccAzureRMServiceBusTopicAuthorizationRule_manage(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_servicebus_namespace" "test" { + name = "acctestservicebusnamespace-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + sku = "Standard" +} + +resource "azurerm_servicebus_topic" "test" { + name = "acctestservicebustopic-%d" + namespace_name = "${azurerm_servicebus_namespace.test.name}" + resource_group_name = "${azurerm_resource_group.test.name}" +} + +resource "azurerm_servicebus_topic_authorization_rule" "test" { + name = "acctestservicebustopicrule-%d" + namespace_name = "${azurerm_servicebus_namespace.test.name}" + topic_name = "${azurerm_servicebus_topic.test.name}" + resource_group_name = "${azurerm_resource_group.test.name}" + listen = true + send = true + manage = true +} +`, rInt, location, rInt, rInt, rInt) +} diff --git a/azurerm/resource_arm_virtual_network_gateway_connection.go b/azurerm/resource_arm_virtual_network_gateway_connection.go index d15f83c71a06..ac59138213e6 100644 --- a/azurerm/resource_arm_virtual_network_gateway_connection.go +++ b/azurerm/resource_arm_virtual_network_gateway_connection.go @@ -79,13 +79,19 @@ func resourceArmVirtualNetworkGatewayConnection() *schema.Resource { "enable_bgp": { Type: schema.TypeBool, Optional: true, - Default: false, + Computed: true, + }, + + "use_policy_based_traffic_selectors": { + Type: schema.TypeBool, + Optional: true, + Computed: true, }, "routing_weight": { Type: schema.TypeInt, Optional: true, - Default: 10, + Computed: true, }, "shared_key": { @@ -94,6 +100,109 @@ func resourceArmVirtualNetworkGatewayConnection() *schema.Resource { Sensitive: true, }, + "ipsec_policy": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "dh_group": { + Type: schema.TypeString, + Required: true, + DiffSuppressFunc: ignoreCaseDiffSuppressFunc, + ValidateFunc: validation.StringInSlice([]string{ + string(network.DHGroup1), + string(network.DHGroup14), + string(network.DHGroup2), + string(network.DHGroup2048), + string(network.DHGroup24), + string(network.ECP256), + string(network.ECP384), + string(network.None), + }, true), + }, + "ike_encryption": { + Type: schema.TypeString, + Required: true, + DiffSuppressFunc: ignoreCaseDiffSuppressFunc, + ValidateFunc: validation.StringInSlice([]string{ + string(network.AES128), + string(network.AES192), + string(network.AES256), + string(network.DES), + string(network.DES3), + }, true), + }, + "ike_integrity": { + Type: schema.TypeString, + Required: true, + DiffSuppressFunc: ignoreCaseDiffSuppressFunc, + ValidateFunc: validation.StringInSlice([]string{ + string(network.MD5), + string(network.SHA1), + string(network.SHA256), + string(network.SHA384), + }, true), + }, + "ipsec_encryption": { + Type: schema.TypeString, + Required: true, + DiffSuppressFunc: ignoreCaseDiffSuppressFunc, + ValidateFunc: validation.StringInSlice([]string{ + string(network.IpsecEncryptionAES128), + string(network.IpsecEncryptionAES192), + string(network.IpsecEncryptionAES256), + string(network.IpsecEncryptionDES), + string(network.IpsecEncryptionDES3), + string(network.IpsecEncryptionGCMAES128), + string(network.IpsecEncryptionGCMAES192), + string(network.IpsecEncryptionGCMAES256), + string(network.IpsecEncryptionNone), + }, true), + }, + "ipsec_integrity": { + Type: schema.TypeString, + Required: true, + DiffSuppressFunc: ignoreCaseDiffSuppressFunc, + ValidateFunc: validation.StringInSlice([]string{ + string(network.IpsecIntegrityGCMAES128), + string(network.IpsecIntegrityGCMAES192), + string(network.IpsecIntegrityGCMAES256), + string(network.IpsecIntegrityMD5), + string(network.IpsecIntegritySHA1), + string(network.IpsecIntegritySHA256), + }, true), + }, + "pfs_group": { + Type: schema.TypeString, + Required: true, + DiffSuppressFunc: ignoreCaseDiffSuppressFunc, + ValidateFunc: validation.StringInSlice([]string{ + string(network.PfsGroupECP256), + string(network.PfsGroupECP384), + string(network.PfsGroupNone), + string(network.PfsGroupPFS1), + string(network.PfsGroupPFS2), + string(network.PfsGroupPFS2048), + string(network.PfsGroupPFS24), + }, true), + }, + "sa_datasize": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + ValidateFunc: validation.IntAtLeast(1024), + }, + "sa_lifetime": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + ValidateFunc: validation.IntAtLeast(300), + }, + }, + }, + }, + "tags": tagsSchema(), }, } @@ -180,7 +289,9 @@ func resourceArmVirtualNetworkGatewayConnectionRead(d *schema.ResourceData, meta d.Set("virtual_network_gateway_id", conn.VirtualNetworkGateway1.ID) } - d.Set("authorization_key", conn.AuthorizationKey) + if conn.AuthorizationKey != nil { + d.Set("authorization_key", conn.AuthorizationKey) + } if conn.Peer != nil { d.Set("express_route_circuit_id", conn.Peer.ID) @@ -194,9 +305,29 @@ func resourceArmVirtualNetworkGatewayConnectionRead(d *schema.ResourceData, meta d.Set("local_network_gateway_id", conn.LocalNetworkGateway2.ID) } - d.Set("enable_bgp", conn.EnableBgp) - d.Set("routing_weight", conn.RoutingWeight) - d.Set("shared_key", conn.SharedKey) + if conn.EnableBgp != nil { + d.Set("enable_bgp", conn.EnableBgp) + } + + if conn.UsePolicyBasedTrafficSelectors != nil { + d.Set("use_policy_based_traffic_selectors", conn.UsePolicyBasedTrafficSelectors) + } + + if conn.RoutingWeight != nil { + d.Set("routing_weight", conn.RoutingWeight) + } + + if conn.SharedKey != nil { + d.Set("shared_key", conn.SharedKey) + } + + if conn.IpsecPolicies != nil { + ipsecPolicies := flattenArmVirtualNetworkGatewayConnectionIpsecPolicies(conn.IpsecPolicies) + + if err := d.Set("ipsec_policy", ipsecPolicies); err != nil { + return fmt.Errorf("Error setting `ipsec_policy`: %+v", err) + } + } flattenAndSetTags(d, resp.Tags) @@ -227,11 +358,9 @@ func resourceArmVirtualNetworkGatewayConnectionDelete(d *schema.ResourceData, me func getArmVirtualNetworkGatewayConnectionProperties(d *schema.ResourceData) (*network.VirtualNetworkGatewayConnectionPropertiesFormat, error) { connectionType := network.VirtualNetworkGatewayConnectionType(d.Get("type").(string)) - enableBgp := d.Get("enable_bgp").(bool) props := &network.VirtualNetworkGatewayConnectionPropertiesFormat{ ConnectionType: connectionType, - EnableBgp: &enableBgp, } if v, ok := d.GetOk("virtual_network_gateway_id"); ok { @@ -294,14 +423,22 @@ func getArmVirtualNetworkGatewayConnectionProperties(d *schema.ResourceData) (*n } } + props.EnableBgp = utils.Bool(d.Get("enable_bgp").(bool)) + + props.UsePolicyBasedTrafficSelectors = utils.Bool(d.Get("use_policy_based_traffic_selectors").(bool)) + if v, ok := d.GetOk("routing_weight"); ok { routingWeight := int32(v.(int)) props.RoutingWeight = &routingWeight } if v, ok := d.GetOk("shared_key"); ok { - sharedKey := v.(string) - props.SharedKey = &sharedKey + props.SharedKey = utils.String(v.(string)) + } + + if v, ok := d.GetOk("ipsec_policy"); ok { + ipsecPolicies := v.([]interface{}) + props.IpsecPolicies = expandArmVirtualNetworkGatewayConnectionIpsecPolicies(ipsecPolicies) } if props.ConnectionType == network.ExpressRoute { @@ -339,3 +476,79 @@ func resourceGroupAndVirtualNetworkGatewayConnectionFromId(virtualNetworkGateway return resGroup, name, nil } + +func expandArmVirtualNetworkGatewayConnectionIpsecPolicies(schemaIpsecPolicies []interface{}) *[]network.IpsecPolicy { + ipsecPolicies := make([]network.IpsecPolicy, 0, len(schemaIpsecPolicies)) + + for _, d := range schemaIpsecPolicies { + schemaIpsecPolicy := d.(map[string]interface{}) + ipsecPolicy := &network.IpsecPolicy{} + + if dhGroup, ok := schemaIpsecPolicy["dh_group"].(string); ok && dhGroup != "" { + ipsecPolicy.DhGroup = network.DhGroup(dhGroup) + } + + if ikeEncryption, ok := schemaIpsecPolicy["ike_encryption"].(string); ok && ikeEncryption != "" { + ipsecPolicy.IkeEncryption = network.IkeEncryption(ikeEncryption) + } + + if ikeIntegrity, ok := schemaIpsecPolicy["ike_integrity"].(string); ok && ikeIntegrity != "" { + ipsecPolicy.IkeIntegrity = network.IkeIntegrity(ikeIntegrity) + } + + if ipsecEncryption, ok := schemaIpsecPolicy["ipsec_encryption"].(string); ok && ipsecEncryption != "" { + ipsecPolicy.IpsecEncryption = network.IpsecEncryption(ipsecEncryption) + } + + if ipsecIntegrity, ok := schemaIpsecPolicy["ipsec_integrity"].(string); ok && ipsecIntegrity != "" { + ipsecPolicy.IpsecIntegrity = network.IpsecIntegrity(ipsecIntegrity) + } + + if pfsGroup, ok := schemaIpsecPolicy["pfs_group"].(string); ok && pfsGroup != "" { + ipsecPolicy.PfsGroup = network.PfsGroup(pfsGroup) + } + + if v, ok := schemaIpsecPolicy["sa_datasize"].(int); ok { + saDatasize := int32(v) + ipsecPolicy.SaDataSizeKilobytes = &saDatasize + } + + if v, ok := schemaIpsecPolicy["sa_lifetime"].(int); ok { + saLifetime := int32(v) + ipsecPolicy.SaLifeTimeSeconds = &saLifetime + } + + ipsecPolicies = append(ipsecPolicies, *ipsecPolicy) + } + + return &ipsecPolicies +} + +func flattenArmVirtualNetworkGatewayConnectionIpsecPolicies(ipsecPolicies *[]network.IpsecPolicy) []interface{} { + schemaIpsecPolicies := make([]interface{}, 0) + + if ipsecPolicies != nil { + for _, ipsecPolicy := range *ipsecPolicies { + schemaIpsecPolicy := make(map[string]interface{}) + + schemaIpsecPolicy["dh_group"] = string(ipsecPolicy.DhGroup) + schemaIpsecPolicy["ike_encryption"] = string(ipsecPolicy.IkeEncryption) + schemaIpsecPolicy["ike_integrity"] = string(ipsecPolicy.IkeIntegrity) + schemaIpsecPolicy["ipsec_encryption"] = string(ipsecPolicy.IpsecEncryption) + schemaIpsecPolicy["ipsec_integrity"] = string(ipsecPolicy.IpsecIntegrity) + schemaIpsecPolicy["pfs_group"] = string(ipsecPolicy.PfsGroup) + + if ipsecPolicy.SaDataSizeKilobytes != nil { + schemaIpsecPolicy["sa_datasize"] = int(*ipsecPolicy.SaDataSizeKilobytes) + } + + if ipsecPolicy.SaLifeTimeSeconds != nil { + schemaIpsecPolicy["sa_lifetime"] = int(*ipsecPolicy.SaLifeTimeSeconds) + } + + schemaIpsecPolicies = append(schemaIpsecPolicies, schemaIpsecPolicy) + } + } + + return schemaIpsecPolicies +} diff --git a/azurerm/resource_arm_virtual_network_gateway_connection_test.go b/azurerm/resource_arm_virtual_network_gateway_connection_test.go index fa58f8d84ba5..9d9fc8bb0a2f 100644 --- a/azurerm/resource_arm_virtual_network_gateway_connection_test.go +++ b/azurerm/resource_arm_virtual_network_gateway_connection_test.go @@ -56,6 +56,25 @@ func TestAccAzureRMVirtualNetworkGatewayConnection_vnettonet(t *testing.T) { }) } +func TestAccAzureRMVirtualNetworkGatewayConnection_ipsecpolicy(t *testing.T) { + ri := acctest.RandInt() + config := testAccAzureRMVirtualNetworkGatewayConnection_ipsecpolicy(ri, testLocation()) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMVirtualNetworkGatewayConnectionDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMVirtualNetworkGatewayConnectionExists("azurerm_virtual_network_gateway_connection.test"), + ), + }, + }, + }) +} + func TestAccAzureRMVirtualNetworkGatewayConnection_updatingSharedKey(t *testing.T) { firstResourceName := "azurerm_virtual_network_gateway_connection.test_1" secondResourceName := "azurerm_virtual_network_gateway_connection.test_2" @@ -156,7 +175,7 @@ resource "azurerm_resource_group" "test" { } resource "azurerm_virtual_network" "test" { - name = "test-${var.random}" + name = "acctestvn-${var.random}" location = "${azurerm_resource_group.test.location}" resource_group_name = "${azurerm_resource_group.test.name}" address_space = ["10.0.0.0/16"] @@ -170,14 +189,14 @@ resource "azurerm_subnet" "test" { } resource "azurerm_public_ip" "test" { - name = "test-${var.random}" + name = "acctest-${var.random}" location = "${azurerm_resource_group.test.location}" resource_group_name = "${azurerm_resource_group.test.name}" public_ip_address_allocation = "Dynamic" } resource "azurerm_virtual_network_gateway" "test" { - name = "test-${var.random}" + name = "acctest-${var.random}" location = "${azurerm_resource_group.test.location}" resource_group_name = "${azurerm_resource_group.test.name}" @@ -194,7 +213,7 @@ resource "azurerm_virtual_network_gateway" "test" { } resource "azurerm_local_network_gateway" "test" { - name = "test-${var.random}" + name = "acctest-${var.random}" location = "${azurerm_resource_group.test.location}" resource_group_name = "${azurerm_resource_group.test.name}" @@ -203,7 +222,7 @@ resource "azurerm_local_network_gateway" "test" { } resource "azurerm_virtual_network_gateway_connection" "test" { - name = "test-${var.random}" + name = "acctest-${var.random}" location = "${azurerm_resource_group.test.location}" resource_group_name = "${azurerm_resource_group.test.name}" @@ -236,7 +255,7 @@ resource "azurerm_resource_group" "test_1" { } resource "azurerm_virtual_network" "test_1" { - name = "acctest-${var.random1}" + name = "acctestvn-${var.random1}" location = "${azurerm_resource_group.test_1.location}" resource_group_name = "${azurerm_resource_group.test_1.name}" address_space = ["10.0.0.0/16"] @@ -341,3 +360,89 @@ resource "azurerm_virtual_network_gateway_connection" "test_2" { } `, rInt, rInt2, sharedKey, location, altLocation) } + +func testAccAzureRMVirtualNetworkGatewayConnection_ipsecpolicy(rInt int, location string) string { + return fmt.Sprintf(` +variable "random" { + default = "%d" +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-${var.random}" + location = "%s" +} + +resource "azurerm_virtual_network" "test" { + name = "acctestvn-${var.random}" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + address_space = ["10.0.0.0/16"] +} + +resource "azurerm_subnet" "test" { + name = "GatewaySubnet" + resource_group_name = "${azurerm_resource_group.test.name}" + virtual_network_name = "${azurerm_virtual_network.test.name}" + address_prefix = "10.0.1.0/24" +} + +resource "azurerm_public_ip" "test" { + name = "acctest-${var.random}" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + public_ip_address_allocation = "Dynamic" +} + +resource "azurerm_virtual_network_gateway" "test" { + name = "acctest-${var.random}" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + + type = "Vpn" + vpn_type = "RouteBased" + sku = "VpnGw1" + + ip_configuration { + name = "vnetGatewayConfig" + public_ip_address_id = "${azurerm_public_ip.test.id}" + private_ip_address_allocation = "Dynamic" + subnet_id = "${azurerm_subnet.test.id}" + } +} + +resource "azurerm_local_network_gateway" "test" { + name = "acctest-${var.random}" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + + gateway_address = "168.62.225.23" + address_space = ["10.1.1.0/24"] +} + +resource "azurerm_virtual_network_gateway_connection" "test" { + name = "acctest-${var.random}" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + + type = "IPsec" + virtual_network_gateway_id = "${azurerm_virtual_network_gateway.test.id}" + local_network_gateway_id = "${azurerm_local_network_gateway.test.id}" + + use_policy_based_traffic_selectors = true + routing_weight = 20 + + ipsec_policy { + dh_group = "DHGroup14" + ike_encryption = "AES256" + ike_integrity = "SHA256" + ipsec_encryption = "AES256" + ipsec_integrity = "SHA256" + pfs_group = "PFS2048" + sa_datasize = 102400000 + sa_lifetime = 27000 + } + + shared_key = "4-v3ry-53cr37-1p53c-5h4r3d-k3y" +} +`, rInt, location) +} diff --git a/examples/aci-linux-multi/main.tf b/examples/aci-linux-multi/main.tf index ae735c2bb023..6d525b1a4e22 100644 --- a/examples/aci-linux-multi/main.tf +++ b/examples/aci-linux-multi/main.tf @@ -3,7 +3,7 @@ resource "azurerm_resource_group" "aci-rg" { location = "${var.resource_group_location}" } -#an attempt to keep the aci container group name (an dns label) somewhat omunique +#an attempt to keep the aci container group name (and dns label) somewhat unique resource "random_integer" "random_int" { min = 100 max = 999 diff --git a/examples/kubernetes-cluster/main.tf b/examples/kubernetes-cluster/main.tf new file mode 100644 index 000000000000..898312caa5fb --- /dev/null +++ b/examples/kubernetes-cluster/main.tf @@ -0,0 +1,39 @@ +resource "azurerm_resource_group" "akc-rg" { + name = "${var.resource_group_name}" + location = "${var.resource_group_location}" +} + +#an attempt to keep the aci container group name (and dns label) somewhat unique +resource "random_integer" "random_int" { + min = 100 + max = 999 +} + +resource "azurerm_kubernetes_cluster" "aks_container" { + name = "akc-${random_integer.random_int.result}" + location = "${var.resource_group_location}" + dns_prefix = "akc-${random_integer.random_int.result}" + + resource_group_name = "${azurerm_resource_group.akc-rg.name}" + kubernetes_version = "1.8.7" + + + linux_profile { + admin_username = "${var.linux_admin_username}" + ssh_key { + key_data = "${var.linux_admin_ssh_publickey}" + } + } + + agent_pool_profile { + name = "agentpool" + count = "2" + vm_size = "Standard_DS2_v2" + os_type = "Linux" + } + + service_principal { + client_id = "${var.client_id}" + client_secret = "${var.client_secret}" + } +} \ No newline at end of file diff --git a/examples/kubernetes-cluster/outputs.tf b/examples/kubernetes-cluster/outputs.tf new file mode 100644 index 000000000000..eb206a8c2ba2 --- /dev/null +++ b/examples/kubernetes-cluster/outputs.tf @@ -0,0 +1,4 @@ + +output "id" { + value = "${azurerm_kubernetes_cluster.aks_container.id}" +} diff --git a/examples/kubernetes-cluster/variables.tf b/examples/kubernetes-cluster/variables.tf new file mode 100644 index 000000000000..eb2648c13c3c --- /dev/null +++ b/examples/kubernetes-cluster/variables.tf @@ -0,0 +1,37 @@ +variable "name" { + type = "string" + description = "Name of this cluster." + default = "akc-example" +} + +variable "client_id" { + type = "string" + description = "Client ID" +} + +variable "client_secret" { + type = "string" + description = "Client secret." +} + +variable "resource_group_name" { + type = "string" + description = "Name of the azure resource group." + default = "akc-rg" +} + +variable "resource_group_location" { + type = "string" + description = "Location of the azure resource group." + default = "eastus" +} + +variable "linux_admin_username" { + type = "string" + description = "User name for authentication to the Kubernetes linux agent virtual machines in the cluster." +} + +variable "linux_admin_ssh_publickey" { + type = "string" + description = "Configure all the linux virtual machines in the cluster with the SSH RSA public key string. The key should include three parts, for example 'ssh-rsa AAAAB...snip...UcyupgH azureuser@linuxvm'" +} \ No newline at end of file diff --git a/examples/vm-from-managed-image/README.md b/examples/vm-from-managed-image/README.md new file mode 100644 index 000000000000..5352a45c9b6c --- /dev/null +++ b/examples/vm-from-managed-image/README.md @@ -0,0 +1,3 @@ +# Virtual Machine from a Managed Image + +Creates a Virtual Machine from an Azure Managed Image (Golden Image) diff --git a/examples/vm-from-managed-image/main.tf b/examples/vm-from-managed-image/main.tf new file mode 100644 index 000000000000..c3aa84d9caa5 --- /dev/null +++ b/examples/vm-from-managed-image/main.tf @@ -0,0 +1,139 @@ +# provider "azurerm" { +# subscription_id = "REPLACE-WITH-YOUR-SUBSCRIPTION-ID" +# client_id = "REPLACE-WITH-YOUR-CLIENT-ID" +# client_secret = "REPLACE-WITH-YOUR-CLIENT-SECRET" +# tenant_id = "REPLACE-WITH-YOUR-TENANT-ID" +# } + +locals { + virtual_machine_name = "${var.prefix}vm" +} + +# Locate the existing custom/golden image +data "azurerm_image" "search" { + name = "${var.image_name}" + resource_group_name = "${var.image_resource_group}" +} + +output "image_id" { + value = "${data.azurerm_image.search.id}" +} + +# Create a Resource Group for the new Virtual Machine. +resource "azurerm_resource_group" "main" { + name = "${var.prefix}-resources" + location = "${var.location}" +} + +# Create a Virtual Network within the Resource Group +resource "azurerm_virtual_network" "main" { + name = "${var.prefix}-network" + address_space = ["172.16.0.0/16"] + resource_group_name = "${azurerm_resource_group.main.name}" + location = "${azurerm_resource_group.main.location}" +} + +# Create a Subnet within the Virtual Network +resource "azurerm_subnet" "internal" { + name = "internal" + virtual_network_name = "${azurerm_virtual_network.main.name}" + resource_group_name = "${azurerm_resource_group.main.name}" + address_prefix = "172.16.1.0/24" +} + +# Create a Public IP for the Virtual Machine +resource "azurerm_public_ip" "main" { + name = "${var.prefix}-pip" + location = "${azurerm_resource_group.main.location}" + resource_group_name = "${azurerm_resource_group.main.name}" + public_ip_address_allocation = "dynamic" +} + +# Create a Network Security Group with some rules +resource "azurerm_network_security_group" "main" { + name = "${var.prefix}-nsg" + location = "${azurerm_resource_group.main.location}" + resource_group_name = "${azurerm_resource_group.main.name}" + + security_rule { + name = "allow_SSH" + description = "Allow SSH access" + priority = 100 + direction = "Inbound" + access = "Allow" + protocol = "Tcp" + source_port_range = "*" + destination_port_range = "22" + source_address_prefix = "*" + destination_address_prefix = "*" + } + + security_rule { + name = "allow_RDP" + description = "Allow RDP access" + priority = 110 + direction = "Inbound" + access = "Allow" + protocol = "Tcp" + source_port_range = "*" + destination_port_range = "3389" + source_address_prefix = "*" + destination_address_prefix = "*" + } +} + +# Create a network interface for VMs and attach the PIP and the NSG +resource "azurerm_network_interface" "main" { + name = "${var.prefix}-nic" + location = "${azurerm_resource_group.main.location}" + resource_group_name = "${azurerm_resource_group.main.name}" + network_security_group_id = "${azurerm_network_security_group.main.id}" + + ip_configuration { + name = "primary" + subnet_id = "${azurerm_subnet.internal.id}" + private_ip_address_allocation = "dynamic" + public_ip_address_id = "${azurerm_public_ip.main.id}" + } +} + +# Create a new Virtual Machine based on the Golden Image +resource "azurerm_virtual_machine" "vm" { + name = "${local.virtual_machine_name}" + location = "${azurerm_resource_group.main.location}" + resource_group_name = "${azurerm_resource_group.main.name}" + network_interface_ids = ["${azurerm_network_interface.main.id}"] + vm_size = "Standard_F2" + delete_os_disk_on_termination = true + delete_data_disks_on_termination = true + + storage_image_reference { + id = "${data.azurerm_image.search.id}" + } + + storage_os_disk { + name = "${local.virtual_machine_name}-osdisk" + caching = "ReadWrite" + create_option = "FromImage" + managed_disk_type = "Premium_LRS" + disk_size_gb = "40" + } + + storage_data_disk { + name = "${local.virtual_machine_name}-data1" + managed_disk_type = "Premium_LRS" + create_option = "Empty" + lun = 0 + disk_size_gb = "1024" + } + + os_profile { + computer_name = "${local.virtual_machine_name}" + admin_username = "${var.admin_username}" + admin_password = "${var.admin_password}" + } + + os_profile_linux_config { + disable_password_authentication = false + } +} diff --git a/examples/vm-from-managed-image/variables.tf b/examples/vm-from-managed-image/variables.tf new file mode 100644 index 000000000000..cb55d2aead79 --- /dev/null +++ b/examples/vm-from-managed-image/variables.tf @@ -0,0 +1,23 @@ +variable "image_name" { + description = "The name of the existing Golden Image" +} + +variable "image_resource_group" { + description = "The name of the Resource Group where the Golden Image is located." +} + +variable "prefix" { + description = "The prefix used for any resources used, must be an alphanumberic string" +} + +variable "location" { + description = "The location where the Resources will be provisioned. This needs to be the same as where the Image exists." +} + +variable "admin_username" { + description = "The username associated with the local administrator account on the Virtual Machine" +} + +variable "admin_password" { + description = "The password associated with the local administrator account on the Virtual Machine" +} diff --git a/website/azurerm.erb b/website/azurerm.erb index e551a543e089..cdb56ffd271f 100644 --- a/website/azurerm.erb +++ b/website/azurerm.erb @@ -17,12 +17,20 @@