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 @@ > Authenticating via a Service Principal (Shared Account) + + > + Authenticating via Managed Service Identity + > Data Sources @@ -458,8 +470,12 @@ > - azurerm_servicebus_topic - + azurerm_servicebus_topic + + + > + azurerm_servicebus_topic_authorization_rule + @@ -480,6 +496,10 @@ azurerm_application_gateway + > + azurerm_application_security_group + + > azurerm_express_route_circuit diff --git a/website/docs/authenticating_via_azure_cli.html.markdown b/website/docs/authenticating_via_azure_cli.html.markdown index dfc072a8cb76..19a5ca52dcaf 100644 --- a/website/docs/authenticating_via_azure_cli.html.markdown +++ b/website/docs/authenticating_via_azure_cli.html.markdown @@ -3,7 +3,7 @@ layout: "azurerm" page_title: "Azure Provider: Authenticating via the Azure CLI" sidebar_current: "docs-azurerm-index-authentication-azure-cli" description: |- - This guide will cover how to use the Azure CLI provide authentication for the Azure Provider. + This guide will cover how to use the Azure CLI to provide authentication for the Azure Provider. --- @@ -11,17 +11,17 @@ description: |- Terraform supports authenticating to Azure through a Service Principal or the Azure CLI. -We recommend [using a Service Principal when running in a Shared Environment](authenticating_via_service_principal.html) (such as within a CI server/automation) - and authenticating via the Azure CLI when you're running Terraform locally. +We recommend [using a Service Principal when running in a shared environment](authenticating_via_service_principal.html) (such as within a CI server/automation) - and authenticating via the Azure CLI when you're running Terraform locally. When authenticating via the Azure CLI, Terraform will automatically connect to the Default Subscription - this can be changed by using the Azure CLI - and is documented below. ## Configuring the Azure CLI -~> **Note:** There are multiple versions of the Azure CLI's - the latest version is known as [the Azure CLI 2.0 (Python)](https://github.com/Azure/azure-cli) and [the older Azure CLI (Node.JS)](https://github.com/Azure/azure-xplat-cli). While Terraform currently supports both - we highly recommend users upgrade to the Azure CLI 2.0 (Python) if possible. +~> **Note:** There are multiple versions of the Azure CLI - the latest version is known as [the Azure CLI 2.0 (Python)](https://github.com/Azure/azure-cli) and [the older Azure CLI (Node.JS)](https://github.com/Azure/azure-xplat-cli). While Terraform currently supports both - we highly recommend users upgrade to the Azure CLI 2.0 (Python) if possible. This guide assumes that you have [the Azure CLI 2.0 (Python)](https://github.com/Azure/azure-cli) installed. -~> **Note:** if you're using the **China**, **German** or **Government** Azure Clouds - you'll need to first configure the Azure CLI to work with that Cloud. You can do this by running: +~> **Note:** If you're using the **China**, **German** or **Government** Azure Clouds - you'll need to first configure the Azure CLI to work with that Cloud. You can do this by running: ```shell $ az cloud set --name AzureChinaCloud|AzureGermanCloud|AzureUSGovernment @@ -47,7 +47,7 @@ Once logged in - it's possible to list the Subscriptions associated with the acc $ az account list ``` -The output (similar to below) will display one or more Subscriptions - with the `ID` field being the Subscription ID. +The output (similar to below) will display one or more Subscriptions - with the `id` field being the Subscription ID. ```json [ diff --git a/website/docs/authenticating_via_msi.html.markdown b/website/docs/authenticating_via_msi.html.markdown new file mode 100644 index 000000000000..4060f94b7810 --- /dev/null +++ b/website/docs/authenticating_via_msi.html.markdown @@ -0,0 +1,96 @@ +--- +layout: "azurerm" +page_title: "AzureRM: Authenticating via Managed Service Identity" +sidebar_current: "docs-azurerm-index-authentication-msi" +description: |- + The Azure Resource Manager provider supports authenticating via multiple means. This guide will cover configuring a Managed Service Identity which can be used to access Azure Resource Manager. + +--- + +# Authenticating to Azure Resource Manager using Managed Service Identity + +Terraform supports authenticating to Azure through Managed Service Identity, Service Principal or the Azure CLI. + +Managed Service Identity can be used to access other Azure Services from within a Virtual Machine in Azure instead of specifying a Service Principal or Azure CLI credentials. + +## Configuring Managed Service Identity + +Managed Service Identity allows an Azure virtual machine to retrieve a token to access the Azure API without needing to pass in credentials. This works by creating a service principal in Azure Active Directory that is associated to a virtual machine. This service principal can then be granted permissions to Azure resources. +There are various ways to configure managed service identity - see the [Microsoft documentation](https://docs.microsoft.com/en-us/azure/active-directory/msi-overview) for details. +You can then run Terraform from the MSI enabled virtual machine by setting the `use_msi` provider option to `true`. + +### Configuring Managed Service Identity using Terraform + +Managed service identity can also be configured using Terraform. The following template shows how. Note that for a Linux VM you must use the `ManagedIdentityExtensionForLinux` extension. + +```hcl +resource "azurerm_virtual_machine" "virtual_machine" { + name = "test" + location = "${var.location}" + resource_group_name = "test" + network_interface_ids = ["${azurerm_network_interface.test.id}"] + vm_size = "Standard_DS1_v2" + + identity = { + type = "SystemAssigned" + } + + storage_image_reference { + publisher = "MicrosoftWindowsServer" + offer = "WindowsServer" + sku = "2016-Datacenter-smalldisk" + version = "latest" + } + + storage_os_disk { + name = "test" + caching = "ReadWrite" + create_option = "FromImage" + managed_disk_type = "Standard_LRS" + } + + os_profile { + computer_name = "test" + admin_username = "username" + admin_password = "password" + } + + os_profile_windows_config { + provision_vm_agent = true + enable_automatic_upgrades = false + } +} + +resource "azurerm_virtual_machine_extension" "virtual_machine_extension" { + name = "test" + location = "${var.location}" + resource_group_name = "test" + virtual_machine_name = "${azurerm_virtual_machine.virtual_machine.name}" + publisher = "Microsoft.ManagedIdentity" + type = "ManagedIdentityExtensionForWindows" + type_handler_version = "1.0" + + settings = < **Note**: if you're using the **China**, **German** or **Government** Azure Clouds - you'll need to first configure the Azure CLI to work with that Cloud. You can do this by running: +~> **Note**: If you're using the **China**, **German** or **Government** Azure Clouds - you'll need to first configure the Azure CLI to work with that Cloud. You can do this by running: ```shell $ az cloud set --name AzureChinaCloud|AzureGermanCloud|AzureUSGovernment @@ -42,7 +42,7 @@ Once logged in - it's possible to list the Subscriptions associated with the acc $ az account list ``` -The output (similar to below) will display one or more Subscriptions - with the `ID` field being the `subscription_id` field referenced above. +The output (similar to below) will display one or more Subscriptions - with the `id` field being the `subscription_id` field referenced above. ```json [ @@ -93,13 +93,13 @@ These values map to the Terraform variables like so: --- -Finally - it's possible to test these values work as expected by first logging in: +Finally, it's possible to test these values work as expected by first logging in: ```shell $ az login --service-principal -u CLIENT_ID -p CLIENT_SECRET --tenant TENANT_ID ``` -Once logged in as the Service Principal - we should be able to list the VM Sizes by specifying an Azure region, for example here we use the `West US` region: +Once logged in as the Service Principal - we should be able to list the VM sizes by specifying an Azure region, for example here we use the `West US` region: ```shell $ az vm list-sizes --location westus @@ -120,7 +120,7 @@ There are two tasks needed to create a Service Principal via [the Azure Portal]( ### 1. Creating an Application in Azure Active Directory -Firstly navigate to [the **Azure Active Directory** overview](https://portal.azure.com/#blade/Microsoft_AAD_IAM/ActiveDirectoryMenuBlade/Overview) within the Azure Portal - [then select the **App Registration** blade](https://portal.azure.com/#blade/Microsoft_AAD_IAM/ActiveDirectoryMenuBlade/RegisteredApps/RegisteredApps/Overview) and click **Endpoints** at the top of the **App Registration** blade. A list of URIs will be displayed and you need to located the URI for **OAUTH 2.0 AUTHORIZATION ENDPOINT** which contains a GUID. This is your Tenant ID / the `tenant_id` field mentioned above. +Firstly navigate to [the **Azure Active Directory** overview](https://portal.azure.com/#blade/Microsoft_AAD_IAM/ActiveDirectoryMenuBlade/Overview) within the Azure Portal - [then select the **App Registration** blade](https://portal.azure.com/#blade/Microsoft_AAD_IAM/ActiveDirectoryMenuBlade/RegisteredApps/RegisteredApps/Overview) and click **Endpoints** at the top of the **App Registration** blade. A list of URIs will be displayed and you need to locate the URI for **OAUTH 2.0 AUTHORIZATION ENDPOINT** which contains a GUID. This is your Tenant ID / the `tenant_id` field mentioned above. Next, navigate back to [the **App Registration** blade](https://portal.azure.com/#blade/Microsoft_AAD_IAM/ActiveDirectoryMenuBlade/RegisteredApps/RegisteredApps/Overview) - from here we'll create the Application in Azure Active Directory. To do this click **Add** at the top to add a new Application within Azure Active Directory. On this page, set the following values then press **Create**: @@ -136,7 +136,7 @@ Finally, we can create the `client_secret` by selecting **Keys** and then genera Once the Application exists in Azure Active Directory - we can grant it permissions to modify resources in the Subscription. To do this, [navigate to the **Subscriptions** blade within the Azure Portal](https://portal.azure.com/#blade/Microsoft_Azure_Billing/SubscriptionsBlade), then select the Subscription you wish to use, then click **Access Control (IAM)**, and finally **Add**. -Firstly specify a Role which grants the appropriate permissions needed for the Service Principal (for example, `Contributor` will grant Read/Write on all resources in the Subscription). There's more information about [the built in roles](https://azure.microsoft.com/en-gb/documentation/articles/role-based-access-built-in-roles/) available here. +Firstly, specify a Role which grants the appropriate permissions needed for the Service Principal (for example, `Contributor` will grant Read/Write on all resources in the Subscription). There's more information about [the built in roles available here](https://azure.microsoft.com/en-gb/documentation/articles/role-based-access-built-in-roles/). Secondly, search for and select the name of the Application created in Azure Active Directory to assign it this role - then press **Save**. diff --git a/website/docs/d/application_security_group.html.markdown b/website/docs/d/application_security_group.html.markdown new file mode 100644 index 000000000000..722aad4c71d5 --- /dev/null +++ b/website/docs/d/application_security_group.html.markdown @@ -0,0 +1,44 @@ +--- +layout: "azurerm" +page_title: "Azure Resource Manager: azurerm_application_security_group" +sidebar_current: "docs-azurerm-datasource-network-application-security-group" +description: |- + Get information about an Application Security Group. +--- + +# Data Source: azurerm_application_security_group + +Get information about an Application Security Group. + +-> **Note:** Application Security Groups are currently in Public Preview on an opt-in basis. [More information, including how you can register for the Preview, and which regions Application Security Groups are available in are available here](https://docs.microsoft.com/en-us/azure/virtual-network/create-network-security-group-preview) + +## Example Usage + +```hcl +data "azurerm_application_security_group" "test" { + name = "tf-appsecuritygroup" + resource_group_name = "my-resource-group" +} + +output "application_security_group_id" { + value = "${data.azurerm_application_security_group.test.id}" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - The name of the Application Security Group. + +* `resource_group_name` - The name of the resource group in which the Application Security Group exists. + +## Attributes Reference + +The following attributes are exported: + +* `id` - The ID of the Application Security Group. + +* `location` - The supported Azure location where the Application Security Group exists. + +* `tags` - A mapping of tags to assign to the resource. diff --git a/website/docs/index.html.markdown b/website/docs/index.html.markdown index 9a6e75b16e7b..0a865df9e3e6 100644 --- a/website/docs/index.html.markdown +++ b/website/docs/index.html.markdown @@ -3,7 +3,7 @@ layout: "azurerm" page_title: "Provider: Azure" sidebar_current: "docs-azurerm-index" description: |- - The Azure Provider is used to interact with the many resources supported by Azure Resource Manager (also known as AzureRM) through it's API's. + The Azure Provider is used to interact with the many resources supported by Azure Resource Manager (also known as AzureRM) through its APIs. --- @@ -19,7 +19,7 @@ Use the navigation to the left to read about the available resources. Terraform supports authenticating to Azure through a Service Principal or the Azure CLI. -We recommend [using a Service Principal when running in a Shared Environment](authenticating_via_service_principal.html) (such as within a CI server/automation) - and [authenticating via the Azure CLI](authenticating_via_azure_cli.html) when you're running Terraform locally. +We recommend [using a Service Principal when running in a shared environment](authenticating_via_service_principal.html) (such as within a CI server/automation) - and [authenticating via the Azure CLI](authenticating_via_azure_cli.html) when you're running Terraform locally. ## Example Usage @@ -73,6 +73,13 @@ The following arguments are supported: * `tenant_id` - (Optional) The tenant ID to use. It can also be sourced from the `ARM_TENANT_ID` environment variable. +* `use_msi` - (Optional) Set to true to authenticate using managed service identity. + It can also be sourced from the `ARM_USE_MSI` environment variable. + +* `msi_endpoint` - (Optional) The REST endpoint to retrieve an MSI token from. Terraform + will attempt to discover this automatically but it can be specified manually here. + It can also be sourced from the `ARM_MSI_ENDPOINT` environment variable. + * `environment` - (Optional) The cloud environment to use. It can also be sourced from the `ARM_ENVIRONMENT` environment variable. Supported values are: * `public` (default) diff --git a/website/docs/r/application_gateway.html.markdown b/website/docs/r/application_gateway.html.markdown index 0c357a46c00e..d9f458702199 100644 --- a/website/docs/r/application_gateway.html.markdown +++ b/website/docs/r/application_gateway.html.markdown @@ -6,7 +6,7 @@ description: |- Creates a new application gateway based on a previously created virtual network with configured subnets. --- -# azurerm\_application\_gateway +# azurerm_application_gateway Creates a new application gateway based on a previously created virtual network with configured subnets. @@ -71,7 +71,7 @@ resource "azurerm_application_gateway" "network" { } frontend_ip_configuration { - name = "${azurerm_virtual_network.vnet.name}-feip" + name = "${azurerm_virtual_network.vnet.name}-feip" public_ip_address_id = "${azurerm_public_ip.pip.id}" } @@ -109,35 +109,35 @@ resource "azurerm_application_gateway" "network" { The following arguments are supported: * `name` - (Required) The name of the application gateway. Changing this forces a - new resource to be created. + new resource to be created. * `resource_group_name` - (Required) The name of the resource group in which to - create the application gateway. + create the application gateway. * `location` - (Required) The location/region where the application gateway is - created. Changing this forces a new resource to be created. + created. Changing this forces a new resource to be created. -* `sku` - (Required) Specifies size, tier and capacity of the application gateway. Must be specified once. The `sku` block fields documented below. +* `sku` - (Required) Specifies size, tier and capacity of the application gateway. Must be specified once. The `sku` block fields documented below. -* `gateway_ip_configuration` - (Required) List of subnets that the application gateway is deployed into. The application gateway must be deployed into an existing virtual network/subnet. No other resource can be deployed in a subnet where application gateway is deployed. The `gateway_ip_configuration` block supports fields documented below. +* `gateway_ip_configuration` - (Required) List of subnets that the application gateway is deployed into. The application gateway must be deployed into an existing virtual network/subnet. No other resource can be deployed in a subnet where application gateway is deployed. The `gateway_ip_configuration` block supports fields documented below. -* `frontend_port` - (Required) Front-end port for the application gateway. The `frontend_port` block supports fields documented below. +* `frontend_port` - (Required) Front-end port for the application gateway. The `frontend_port` block supports fields documented below. -* `frontend_ip_configuration` - (Required) Specifies lists of frontend IP configurations. Currently only one Public and/or one Private IP address can be specified. Also one frontendIpConfiguration element can specify either Public or Private IP address, not both. The `frontend_ip_configuration` block supports fields documented below. +* `frontend_ip_configuration` - (Required) Specifies lists of frontend IP configurations. Currently only one Public and/or one Private IP address can be specified. Also one frontendIpConfiguration element can specify either Public or Private IP address, not both. The `frontend_ip_configuration` block supports fields documented below. -* `backend_address_pool` - (Required) Backend pools can be composed of NICs, virtual machine scale sets, public IPs, internal IPs, fully qualified domain names (FQDN), and multi-tenant back-ends like Azure Web Apps. Application Gateway backend pool members are not tied to an availability set. Members of backend pools can be across clusters, data centers, or outside of Azure as long as they have IP connectivity. The `backend_address_pool` block supports fields documented below. +* `backend_address_pool` - (Required) Backend pools can be composed of NICs, virtual machine scale sets, public IPs, internal IPs, fully qualified domain names (FQDN), and multi-tenant back-ends like Azure Web Apps. Application Gateway backend pool members are not tied to an availability set. Members of backend pools can be across clusters, data centers, or outside of Azure as long as they have IP connectivity. The `backend_address_pool` block supports fields documented below. -* `backend_http_settings` - (Required) Related group of backend http and/or https features to be applied when routing to backend address pools. The `backend_http_settings` block supports fields documented below. +* `backend_http_settings` - (Required) Related group of backend http and/or https features to be applied when routing to backend address pools. The `backend_http_settings` block supports fields documented below. -* `http_listener` - (Required) 1 or more listeners specifying port, http or https and SSL certificate (if configuring SSL offload) Each `http_listener` is attached to a `frontend_ip_configuration`. The `http_listener` block supports fields documented below. +* `http_listener` - (Required) 1 or more listeners specifying port, http or https and SSL certificate (if configuring SSL offload) Each `http_listener` is attached to a `frontend_ip_configuration`. The `http_listener` block supports fields documented below. * `probe` - (Optional) Specifies list of URL probes. The `probe` block supports fields documented below. -* `request_routing_rule` - (Required) Request routing rules can be either Basic or Path Based. Request routing rules are order sensitive. The `request_routing_rule` block supports fields documented below. +* `request_routing_rule` - (Required) Request routing rules can be either Basic or Path Based. Request routing rules are order sensitive. The `request_routing_rule` block supports fields documented below. -* `url_path_map` - (Optional) UrlPathMaps give url Path to backend mapping information for PathBasedRouting specified in `request_routing_rule`. The `url_path_map` block supports fields documented below. +* `url_path_map` - (Optional) UrlPathMaps give url Path to backend mapping information for PathBasedRouting specified in `request_routing_rule`. The `url_path_map` block supports fields documented below. -* `authentication_certificate` - (Optional) List of authentication certificates. The `authentication_certificate` block supports fields documented below. +* `authentication_certificate` - (Optional) List of authentication certificates. The `authentication_certificate` block supports fields documented below. * `ssl_certificate` - (Optional) List of ssl certificates. The `ssl_certificate` block supports fields documented below. @@ -148,6 +148,7 @@ The following arguments are supported: The `sku` block supports: * `name` - (Required) Supported values are: + * `Standard_Small` * `Standard_Medium` * `Standard_Large` @@ -155,6 +156,7 @@ The `sku` block supports: * `WAF_Large` * `tier` - (Required) Supported values are: + * `Standard` * `WAF` @@ -201,10 +203,12 @@ The `backend_http_settings` block supports: * `port` - (Required) Backend port for backend address pool. * `protocol` - (Required) Valid values are: + * `Http` * `Https` * `cookie_based_affinity` - (Required) Valid values are: + * `Enabled` * `Disabled` @@ -223,6 +227,7 @@ The `http_listener` block supports: * `frontend_port_name` - (Required) Reference to frontend port. * `protocol` - (Required) Valid values are: + * `Http` * `Https` @@ -231,7 +236,7 @@ The `http_listener` block supports: * `ssl_certificate_name` - (Optional) Reference to ssl certificate. Valid only if protocol is https. * `require_sni` - (Optional) Applicable only if protocol is https. Enables SNI for multi-hosting. -Valid values are: + Valid values are: * true * false @@ -239,13 +244,14 @@ The `probe` block supports: * `name` - (Required) User defined name for a probe. -* `protocol` - (Required) Protocol used to send probe. Valid values are: +* `protocol` - (Required) Protocol used to send probe. Valid values are: + * `Http` * `Https` -* `path` - (Required) Relative path of probe. Valid path starts from '/'. Probe is sent to \{Protocol}://\{host}:\{port}\{path} +* `path` - (Required) Relative path of probe. Valid path starts from '/'. Probe is sent to \{Protocol}://\{host}:\{port}\{path}. The port used will be the same port as defined in the `backend_http_settings`. -* `host` - (Required) Host name to send probe to. +* `host` - (Required) Host name to send probe to. If Application Gateway is configured for a single site, by default the Host name should be specified as ‘127.0.0.1’, unless otherwise configured in custom probe. * `interval` - (Required) Probe interval in seconds. This is the time interval between two consecutive probes. Minimum 1 second and Maximum 86,400 secs. @@ -253,12 +259,12 @@ The `probe` block supports: * `unhealthy_threshold` - (Required) Probe retry count. Backend server is marked down after consecutive probe failure count reaches UnhealthyThreshold. Minimum 1 second and Maximum 20. - The `request_routing_rule` block supports: * `name` - (Required) User defined name for a request routing rule. * `rule_type' - (Required) Routing rule type. Valid values are: + * `Basic` * `PathBasedRouting` @@ -284,7 +290,7 @@ The `path_rule` block supports: * `name` - (Required) User defined name for a path rule. -* `paths` - (Required) The list of path patterns to match. Each must start with / and the only place a * is allowed is at the end following a /. The string fed to the path matcher does not include any text after the first ? or #, and those chars are not allowed here. +* `paths` - (Required) The list of path patterns to match. Each must start with / and the only place a \* is allowed is at the end following a /. The string fed to the path matcher does not include any text after the first ? or #, and those chars are not allowed here. * `backend_address_pool_name` - (Required) Reference to `backend_address_pool_name`. @@ -308,17 +314,17 @@ The `waf_configuration` block supports: * `name` - (Required) User defined name for a web application firewall. -* `firewall_mode` - (Required) Firewall mode. Valid values are: +* `firewall_mode` - (Required) Firewall mode. Valid values are: + * `Detection` * `Prevention` -* `rule_set_type` - (Required) Rule set type. Must be set to `OWASP` +* `rule_set_type` - (Required) Rule set type. Must be set to `OWASP` * `rule_set_version` - (Required) Ruleset version. Supported values: * `2.2.9` * `3.0` - ## Attributes Reference The following attributes are exported: @@ -331,13 +337,10 @@ The following attributes are exported: * `location` - The location/region where the application gateway is created - - ## Import application gateways can be imported using the `resource id`, e.g. -```shell +```shell terraform import azurerm_application_gateway.testApplicationGateway /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/mygroup1/providers/Microsoft.Network/applicationGateways/myGateway1 - ``` diff --git a/website/docs/r/application_security_group.html.markdown b/website/docs/r/application_security_group.html.markdown new file mode 100644 index 000000000000..a078a52c38da --- /dev/null +++ b/website/docs/r/application_security_group.html.markdown @@ -0,0 +1,57 @@ +--- +layout: "azurerm" +page_title: "Azure Resource Manager: azurerm_application_security_group" +sidebar_current: "docs-azurerm-resource-network-application-security-group" +description: |- + Create an Application Security Group. +--- + +# azurerm_application_security_group + +Create an Application Security Group. + +-> **Note:** Application Security Groups are currently in Public Preview on an opt-in basis. [More information, including how you can register for the Preview, and which regions Application Security Groups are available in are available here](https://docs.microsoft.com/en-us/azure/virtual-network/create-network-security-group-preview) + +## Example Usage + +```hcl +resource "azurerm_resource_group" "test" { + name = "tf-test" + location = "West Europe" +} + +resource "azurerm_application_security_group" "test" { + name = "tf-appsecuritygroup" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + tags { + "Hello" = "World" + } +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) Specifies the name of the Application Security Group. Changing this forces a new resource to be created. + +* `resource_group_name` - (Required) The name of the resource group in which to create the Application Security Group. + +* `location` - (Required) Specifies the supported Azure location where the resource exists. Changing this forces a new resource to be created. + +* `tags` - (Optional) A mapping of tags to assign to the resource. + +## Attributes Reference + +The following attributes are exported: + +* `id` - The ID of the Application Security Group. + +## Import + +Application Security Groups can be imported using the `resource id`, e.g. + +```shell +terraform import azurerm_application_security_group.securitygroup1 /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/group1/providers/Microsoft.Network/applicationSecurityGroups/securitygroup1 +``` diff --git a/website/docs/r/kubernetes_cluster.html.markdown b/website/docs/r/kubernetes_cluster.html.markdown index 8651df097afe..aba95543f56e 100644 --- a/website/docs/r/kubernetes_cluster.html.markdown +++ b/website/docs/r/kubernetes_cluster.html.markdown @@ -19,7 +19,7 @@ Creates a managed Kubernetes Cluster (AKS) ```hcl resource "azurerm_resource_group" "test" { name = "acctestRG1" - location = "West US" + location = "East US" } resource "azurerm_kubernetes_cluster" "test" { @@ -79,12 +79,12 @@ The following arguments are supported: `linux_profile` supports the following: -* `admin_username` - (Required) The Admin Username for the Cluster. +* `admin_username` - (Required) The Admin Username for the Cluster. Changing this forces a new resource to be created. * `ssh_key` - (Required) An SSH Key block as documented below. `ssh_key` supports the following: -* `key_data` - (Required) The Public SSH Key used to access the cluster. +* `key_data` - (Required) The Public SSH Key used to access the cluster. Changing this forces a new resource to be created. `agent_pool_profile` supports the following: @@ -105,7 +105,7 @@ The following attributes are exported: * `id` - The Kubernetes Managed Cluster ID. -* `fqdn` - The FQDN of the Azure Kubernetes Managed Cluster. +* `agent_pool_profile.#.fqdn` - The FQDN of the Azure Kubernetes Managed Cluster. ## Import diff --git a/website/docs/r/network_interface.html.markdown b/website/docs/r/network_interface.html.markdown index 484abc2e40a8..4a6cc8427c8f 100644 --- a/website/docs/r/network_interface.html.markdown +++ b/website/docs/r/network_interface.html.markdown @@ -92,6 +92,10 @@ The `ip_configuration` block supports: * `load_balancer_inbound_nat_rules_ids` - (Optional) List of Load Balancer Inbound Nat Rules IDs involving this NIC +* `application_security_group_ids` - (Optional) List of Application Security Group IDs which should be attached to this NIC + +-> **Note:** Application Security Groups are currently in Public Preview on an opt-in basis. [More information, including how you can register for the Preview, and which regions Application Security Groups are available in are available here](https://docs.microsoft.com/en-us/azure/virtual-network/create-network-security-group-preview) + * `primary` - (Optional) Is this the Primary Network Interface? If set to `true` this should be the first `ip_configuration` in the array. ## Attributes Reference diff --git a/website/docs/r/network_security_group.html.markdown b/website/docs/r/network_security_group.html.markdown index 99e12676c085..1f0fb61aaf49 100644 --- a/website/docs/r/network_security_group.html.markdown +++ b/website/docs/r/network_security_group.html.markdown @@ -69,13 +69,21 @@ The `security_rule` block supports: * `protocol` - (Required) Network protocol this rule applies to. Can be `Tcp`, `Udp` or `*` to match both. -* `source_port_range` - (Required) Source Port or Range. Integer or range between `0` and `65535` or `*` to match any. +* `source_port_range` - (Optional) Source Port or Range. Integer or range between `0` and `65535` or `*` to match any. This is required if `source_port_ranges` is not specified. -* `destination_port_range` - (Required) Destination Port or Range. Integer or range between `0` and `65535` or `*` to match any. +* `source_port_ranges` - (Optional) List of source ports or port ranges. This is required if `source_port_range` is not specified. -* `source_address_prefix` - (Required) CIDR or source IP range or * to match any IP. Tags such as `VirtualNetwork`, `AzureLoadBalancer` and `Internet` can also be used. +* `destination_port_range` - (Optional) Destination Port or Range. Integer or range between `0` and `65535` or `*` to match any. This is required if `destination_port_ranges` is not specified. -* `destination_address_prefix` - (Required) CIDR or destination IP range or * to match any IP. Tags such as `VirtualNetwork`, `AzureLoadBalancer` and `Internet` can also be used. +* `destination_port_ranges` - (Optional) List of destination ports or port ranges. This is required if `destination_port_range` is not specified. + +* `source_address_prefix` - (Optional) CIDR or source IP range or * to match any IP. Tags such as ‘VirtualNetwork’, ‘AzureLoadBalancer’ and ‘Internet’ can also be used. This is required if `source_address_prefixes` is not specified. + +* `source_address_prefixes` - (Optional) List of source address prefixes. Tags may not be used. This is required if `source_address_prefix` is not specified. + +* `destination_address_prefix` - (Optional) CIDR or destination IP range or * to match any IP. Tags such as ‘VirtualNetwork’, ‘AzureLoadBalancer’ and ‘Internet’ can also be used. This is required if `destination_address_prefixes` is not specified. + +* `destination_address_prefixes` - (Optional) List of destination address prefixes. Tags may not be used. This is required if `destination_address_prefix` is not specified. * `access` - (Required) Specifies whether network traffic is allowed or denied. Possible values are `Allow` and `Deny`. diff --git a/website/docs/r/servicebus_topic_authorization_rule.html.markdown b/website/docs/r/servicebus_topic_authorization_rule.html.markdown new file mode 100644 index 000000000000..2c98a44d8c0e --- /dev/null +++ b/website/docs/r/servicebus_topic_authorization_rule.html.markdown @@ -0,0 +1,94 @@ +--- +layout: "azurerm" +page_title: "Azure Resource Manager: azurerm_servicebus_topic_authorization_rule" +sidebar_current: "docs-azurerm-resource-servicebus-topic-authorization-rule" +description: |- + Creates a new ServiceBus Topic authorization Rule within a ServiceBus Topic. +--- + +# azurerm_servicebus_topic_authorization_rule + +Creates a new ServiceBus Topic authorization Rule within a ServiceBus Topic. + +## Example Usage + +```hcl +variable "location" { + description = "Azure datacenter to deploy to." + default = "West US" +} + +resource "azurerm_resource_group" "test" { + name = "terraform-servicebus" + location = "${var.location}" +} + +resource "azurerm_servicebus_namespace" "test" { + name = "${var.servicebus_name}" + location = "${var.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + sku = "standard" + + tags { + source = "terraform" + } +} + +resource "azurerm_servicebus_topic" "test" { + name = "testTopic" + resource_group_name = "${azurerm_resource_group.test.name}" + namespace_name = "${azurerm_servicebus_namespace.test.name}" +} + +resource "azurerm_servicebus_topic_authorization_rule" "test" { + name = "examplerule" + 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 +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) Specifies the name of the erviceBus Topic Authorization Rule resource. Changing this forces a new resource to be created. + +* `namespace_name` - (Required) Specifies the name of the ServiceBus Namespace. Changing this forces a new resource to be created. + +* `topic_name` - (Required) Specifies the name of the ServiceBus Topic. Changing this forces a new resource to be created. + +* `resource_group_name` - (Required) The name of the resource group in which the ServiceBus Namespace exists. Changing this forces a new resource to be created. + +~> **NOTE** At least one of the 3 permissions below needs to be set. + +* `listen` - (Optional) Does this Authorization Rule have permissions to Listen to the ServiceBus Topic? Defaults to `false`. + +* `send` - (Optional) Does this Authorization Rule have permissions to Send to the ServiceBus Topic? Defaults to `false`. + +* `manage` - (Optional) Does this Authorization Rule have permissions to Manage to the ServiceBus Topic? When this property is `true` - both `listen` and `send` must be too. Defaults to `false`. + +## Attributes Reference + +The following attributes are exported: + +* `id` - The ServiceBus Topic ID. + +* `primary_key` - The Primary Key for the ServiceBus Topic authorization Rule. + +* `primary_connection_string` - The Primary Connection String for the ServiceBus Topic authorization Rule. + +* `secondary_key` - The Secondary Key for the ServiceBus Topic authorization Rule. + +* `secondary_connection_string` - The Secondary Connection String for the ServiceBus Topic authorization Rule. + +## Import + +ServiceBus Topic authorization rules can be imported using the `resource id`, e.g. + +```shell +terraform import azurerm_servicebus_topic_authorization_rule.rule1 /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/group1/providers/Microsoft.ServiceBus/namespaces/namespace1/topics/topic1/authorizationRules/rule1 +``` diff --git a/website/docs/r/virtual_machine.html.markdown b/website/docs/r/virtual_machine.html.markdown index 0213d772c971..cf4019d33250 100644 --- a/website/docs/r/virtual_machine.html.markdown +++ b/website/docs/r/virtual_machine.html.markdown @@ -10,7 +10,7 @@ description: |- Create a virtual machine. -## Example Usage with Managed Disks (Recommended) +## Example Usage with Managed Disks and Azure Platform Images (Recommended) ```hcl resource "azurerm_resource_group" "test" { @@ -113,6 +113,116 @@ resource "azurerm_virtual_machine" "test" { } ``` +## Example Usage with Managed Disks and Custom Images (Recommended) + +```hcl +#Assume that custom image has been already created in the 'customimage' resource group +data "azurerm_resource_group" "image" { + name = "customimage" +} + +data "azurerm_image" "image" { + name = "myCustomImage" + resource_group_name = "${data.azurerm_resource_group.image.name}" +} + +resource "azurerm_resource_group" "test" { + name = "acctestrg" + location = "West US 2" +} + +resource "azurerm_virtual_network" "test" { + name = "acctvn" + 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 = "acctsub" + resource_group_name = "${azurerm_resource_group.test.name}" + virtual_network_name = "${azurerm_virtual_network.test.name}" + address_prefix = "10.0.2.0/24" +} + +resource "azurerm_network_interface" "test" { + name = "acctni" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + + ip_configuration { + name = "testconfiguration1" + subnet_id = "${azurerm_subnet.test.id}" + private_ip_address_allocation = "dynamic" + } +} + +resource "azurerm_managed_disk" "test" { + name = "datadisk_existing" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + storage_account_type = "Standard_LRS" + create_option = "Empty" + disk_size_gb = "1023" +} + +resource "azurerm_virtual_machine" "test" { + name = "acctvm" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + network_interface_ids = ["${azurerm_network_interface.test.id}"] + vm_size = "Standard_DS1_v2" + + # Uncomment this line to delete the OS disk automatically when deleting the VM + # delete_os_disk_on_termination = true + + # Uncomment this line to delete the data disks automatically when deleting the VM + # delete_data_disks_on_termination = true + + storage_profile_image_reference { + id="${data.azurerm_image.image.id}" + } + + storage_os_disk { + name = "myosdisk1" + caching = "ReadWrite" + create_option = "FromImage" + managed_disk_type = "Standard_LRS" + } + + # Optional data disks + storage_data_disk { + name = "datadisk_new" + managed_disk_type = "Standard_LRS" + create_option = "Empty" + lun = 0 + disk_size_gb = "1023" + } + + storage_data_disk { + name = "${azurerm_managed_disk.test.name}" + managed_disk_id = "${azurerm_managed_disk.test.id}" + create_option = "Attach" + lun = 1 + disk_size_gb = "${azurerm_managed_disk.test.disk_size_gb}" + } + + os_profile { + computer_name = "hostname" + admin_username = "testadmin" + admin_password = "Password1234!" + } + + os_profile_linux_config { + disable_password_authentication = false + } + + tags { + environment = "staging" + } +} +``` + ## Example Usage with Unmanaged Disks ```hcl diff --git a/website/docs/r/virtual_machine_extension.html.markdown b/website/docs/r/virtual_machine_extension.html.markdown index 53ba6b30da8e..41cd3fa9af73 100644 --- a/website/docs/r/virtual_machine_extension.html.markdown +++ b/website/docs/r/virtual_machine_extension.html.markdown @@ -7,11 +7,13 @@ description: |- configuration and run automated tasks. --- -# azurerm\_virtual\_machine\_extension +# azurerm_virtual_machine_extension Creates a new Virtual Machine Extension to provide post deployment configuration and run automated tasks. +~> **Please Note:** The CustomScript extensions for Linux & Windows require that the `commandToExecute` returns a `0` exit code to be classified as successfully deployed. You can achieve this by appending `exit 0` to the end of your `commandToExecute`. + ## Example Usage ```hcl @@ -159,9 +161,13 @@ $ az vm extension image list --location westus -o table * `settings` - (Required) The settings passed to the extension, these are specified as a JSON object in a string. +~> **Please Note:** Certain VM Extensions require that the keys in the `settings` block are case sensitive. If you're seeing unhelpful errors, please ensure the keys are consistent with how Azure is expecting them (for instance, for the `JsonADDomainExtension` extension, the keys are expected to be in `TitleCase`.) + * `protected_settings` - (Optional) The protected_settings passed to the extension, like settings, these are specified as a JSON object in a string. +~> **Please Note:** Certain VM Extensions require that the keys in the `protected_settings` block are case sensitive. If you're seeing unhelpful errors, please ensure the keys are consistent with how Azure is expecting them (for instance, for the `JsonADDomainExtension` extension, the keys are expected to be in `TitleCase`.) + ## Attributes Reference The following attributes are exported: diff --git a/website/docs/r/virtual_network_gateway_connection.html.markdown b/website/docs/r/virtual_network_gateway_connection.html.markdown index b1cbed6fddf1..85327a9397c9 100644 --- a/website/docs/r/virtual_network_gateway_connection.html.markdown +++ b/website/docs/r/virtual_network_gateway_connection.html.markdown @@ -197,7 +197,6 @@ resource "azurerm_virtual_network_gateway_connection" "europe_to_us" { shared_key = "4-v3ry-53cr37-1p53c-5h4r3d-k3y" } - ``` ## Argument Reference @@ -226,7 +225,7 @@ The following arguments are supported: * `authorization_key` - (Optional) The authorization key associated with the Express Route Circuit. This field is required only if the type is an ExpressRoute connection. -§ + * `express_route_circuit_id` - (Optional) The ID of the Express Route Circuit when creating an ExpressRoute connection (i.e. when `type` is `ExpressRoute`). The Express Route Circuit can be in the same or in a different subscription. @@ -248,8 +247,44 @@ The following arguments are supported: * `enable_bgp` - (Optional) If `true`, BGP (Border Gateway Protocol) is enabled for this connection. Defaults to `false`. +* `use_policy_based_traffic_selectors` - (Optional) If `true`, policy-based traffic + selectors are enabled for this connection. Enabling policy-based traffic + selectors requires an `ipsec_policy` block. Defaults to `false`. + +* `ipsec_policy` (Optional) A `ipsec_policy` block which is documented below. + Only a single policy can be defined for a connection. For details on + custom policies refer to [the relevant section in the Azure documentation](https://docs.microsoft.com/en-us/azure/vpn-gateway/vpn-gateway-ipsecikepolicy-rm-powershell). + * `tags` - (Optional) A mapping of tags to assign to the resource. +The `ipsec_policy` block supports: + +* `dh_group` - (Required) The DH group used in IKE phase 1 for initial SA. Valid + options are `DHGroup1`, `DHGroup14`, `DHGroup2`, `DHGroup2048`, `DHGroup24`, + `ECP256`, `ECP384`, or `None`. + +* `ike_encryption` - (Required) The IKE encryption algorithm. Valid + options are `AES128`, `AES192`, `AES256`, `DES`, or `DES3`. + +* `ike_integrity` - (Required) The IKE integrity algorithm. Valid + options are `MD5`, `SHA1`, `SHA256`, or `SHA384`. + +* `ipsec_encryption` - (Required) The IPSec encryption algorithm. Valid + options are `AES128`, `AES192`, `AES256`, `DES`, `DES3`, `GCMAES128`, `GCMAES192`, `GCMAES256`, or `None`. + +* `ipsec_integrity` - (Required) The IPSec integrity algorithm. Valid + options are `GCMAES128`, `GCMAES192`, `GCMAES256`, `MD5`, `SHA1`, or `SHA256`. + +* `pfs_group` - (Required) The DH group used in IKE phase 2 for new child SA. + Valid options are `ECP256`, `ECP384`, `PFS1`, `PFS2`, `PFS2048`, `PFS24`, + or `None`. + +* `sa_datasize` - (Optional) The IPSec SA payload size in KB. Must be at least + `1024` KB. Defaults to `102400000` KB. + +* `sa_lifetime` - (Optional) The IPSec SA lifetime in seconds. Must be at least + `300` seconds. Defaults to `27000` seconds. + ## Attributes Reference The following attributes are exported: