diff --git a/azurerm/resource_arm_service_fabric_cluster.go b/azurerm/resource_arm_service_fabric_cluster.go index 46e0b6603676..3df749b1a12d 100644 --- a/azurerm/resource_arm_service_fabric_cluster.go +++ b/azurerm/resource_arm_service_fabric_cluster.go @@ -107,9 +107,10 @@ func resourceArmServiceFabricCluster() *schema.Resource { }, "certificate": { - Type: schema.TypeList, - Optional: true, - MaxItems: 1, + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + ConflictsWith: []string{"certificate_common_names"}, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "thumbprint": { @@ -128,6 +129,41 @@ func resourceArmServiceFabricCluster() *schema.Resource { }, }, + "certificate_common_names": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + ConflictsWith: []string{"certificate"}, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "common_names": { + Type: schema.TypeSet, + Required: true, + MinItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "certificate_common_name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validate.NoEmptyStrings, + }, + "certificate_issuer_thumbprint": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validate.NoEmptyStrings, + }, + }, + }, + }, + "x509_store_name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validate.NoEmptyStrings, + }, + }, + }, + }, + "reverse_proxy_certificate": { Type: schema.TypeList, Optional: true, @@ -171,34 +207,28 @@ func resourceArmServiceFabricCluster() *schema.Resource { "diagnostics_config": { Type: schema.TypeList, Optional: true, - ForceNew: true, MaxItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "storage_account_name": { Type: schema.TypeString, Required: true, - ForceNew: true, }, "protected_account_key_name": { Type: schema.TypeString, Required: true, - ForceNew: true, }, "blob_endpoint": { Type: schema.TypeString, Required: true, - ForceNew: true, }, "queue_endpoint": { Type: schema.TypeString, Required: true, - ForceNew: true, }, "table_endpoint": { Type: schema.TypeString, Required: true, - ForceNew: true, }, }, }, @@ -229,7 +259,6 @@ func resourceArmServiceFabricCluster() *schema.Resource { "name": { Type: schema.TypeString, Required: true, - ForceNew: true, }, "placement_properties": { Type: schema.TypeMap, @@ -246,17 +275,14 @@ func resourceArmServiceFabricCluster() *schema.Resource { "is_primary": { Type: schema.TypeBool, Required: true, - ForceNew: true, }, "client_endpoint_port": { Type: schema.TypeInt, Required: true, - ForceNew: true, }, "http_endpoint_port": { Type: schema.TypeInt, Required: true, - ForceNew: true, }, "reverse_proxy_endpoint_port": { Type: schema.TypeInt, @@ -267,7 +293,6 @@ func resourceArmServiceFabricCluster() *schema.Resource { Type: schema.TypeString, Optional: true, Default: string(servicefabric.Bronze), - ForceNew: true, ValidateFunc: validation.StringInSlice([]string{ string(servicefabric.Bronze), string(servicefabric.Gold), @@ -278,7 +303,6 @@ func resourceArmServiceFabricCluster() *schema.Resource { "application_ports": { Type: schema.TypeList, Optional: true, - ForceNew: true, Computed: true, MaxItems: 1, Elem: &schema.Resource{ @@ -286,12 +310,10 @@ func resourceArmServiceFabricCluster() *schema.Resource { "start_port": { Type: schema.TypeInt, Required: true, - ForceNew: true, }, "end_port": { Type: schema.TypeInt, Required: true, - ForceNew: true, }, }, }, @@ -300,7 +322,6 @@ func resourceArmServiceFabricCluster() *schema.Resource { "ephemeral_ports": { Type: schema.TypeList, Optional: true, - ForceNew: true, Computed: true, MaxItems: 1, Elem: &schema.Resource{ @@ -308,12 +329,10 @@ func resourceArmServiceFabricCluster() *schema.Resource { "start_port": { Type: schema.TypeInt, Required: true, - ForceNew: true, }, "end_port": { Type: schema.TypeInt, Required: true, - ForceNew: true, }, }, }, @@ -367,9 +386,6 @@ func resourceArmServiceFabricClusterCreateUpdate(d *schema.ResourceData, meta in azureActiveDirectoryRaw := d.Get("azure_active_directory").([]interface{}) azureActiveDirectory := expandServiceFabricClusterAzureActiveDirectory(azureActiveDirectoryRaw) - certificateRaw := d.Get("certificate").([]interface{}) - certificate := expandServiceFabricClusterCertificate(certificateRaw) - reverseProxyCertificateRaw := d.Get("reverse_proxy_certificate").([]interface{}) reverseProxyCertificate := expandServiceFabricClusterReverseProxyCertificate(reverseProxyCertificateRaw) @@ -391,7 +407,7 @@ func resourceArmServiceFabricClusterCreateUpdate(d *schema.ResourceData, meta in ClusterProperties: &servicefabric.ClusterProperties{ AddOnFeatures: addOnFeatures, AzureActiveDirectory: azureActiveDirectory, - Certificate: certificate, + CertificateCommonNames: expandServiceFabricClusterCertificateCommonNames(d), ReverseProxyCertificate: reverseProxyCertificate, ClientCertificateThumbprints: clientCertificateThumbprints, DiagnosticsStorageAccountConfig: diagnostics, @@ -404,6 +420,11 @@ func resourceArmServiceFabricClusterCreateUpdate(d *schema.ResourceData, meta in }, } + if certificateRaw, ok := d.GetOk("certificate"); ok { + certificate := expandServiceFabricClusterCertificate(certificateRaw.([]interface{})) + cluster.ClusterProperties.Certificate = certificate + } + if clusterCodeVersion != "" { cluster.ClusterProperties.ClusterCodeVersion = utils.String(clusterCodeVersion) } @@ -482,6 +503,11 @@ func resourceArmServiceFabricClusterRead(d *schema.ResourceData, meta interface{ return fmt.Errorf("Error setting `certificate`: %+v", err) } + certificateCommonNames := flattenServiceFabricClusterCertificateCommonNames(props.CertificateCommonNames) + if err := d.Set("certificate_common_names", certificateCommonNames); err != nil { + return fmt.Errorf("Error setting `certificate_common_names`: %+v", err) + } + reverseProxyCertificate := flattenServiceFabricClusterReverseProxyCertificate(props.ReverseProxyCertificate) if err := d.Set("reverse_proxy_certificate", reverseProxyCertificate); err != nil { return fmt.Errorf("Error setting `reverse_proxy_certificate`: %+v", err) @@ -644,6 +670,70 @@ func flattenServiceFabricClusterCertificate(input *servicefabric.CertificateDesc return results } +func expandServiceFabricClusterCertificateCommonNames(d *schema.ResourceData) *servicefabric.ServerCertificateCommonNames { + i := d.Get("certificate_common_names").([]interface{}) + if len(i) <= 0 || i[0] == nil { + return nil + } + input := i[0].(map[string]interface{}) + + commonNamesRaw := input["common_names"].(*schema.Set).List() + commonNames := make([]servicefabric.ServerCertificateCommonName, 0) + + for _, commonName := range commonNamesRaw { + commonNameDetails := commonName.(map[string]interface{}) + certificateCommonName := commonNameDetails["certificate_common_name"].(string) + certificateIssuerThumbprint := commonNameDetails["certificate_issuer_thumbprint"].(string) + + commonName := servicefabric.ServerCertificateCommonName{ + CertificateCommonName: &certificateCommonName, + CertificateIssuerThumbprint: &certificateIssuerThumbprint, + } + + commonNames = append(commonNames, commonName) + } + + x509StoreName := input["x509_store_name"].(string) + + output := servicefabric.ServerCertificateCommonNames{ + CommonNames: &commonNames, + X509StoreName: servicefabric.X509StoreName1(x509StoreName), + } + + return &output +} + +func flattenServiceFabricClusterCertificateCommonNames(in *servicefabric.ServerCertificateCommonNames) []interface{} { + if in == nil { + return []interface{}{} + } + + output := make(map[string]interface{}) + + if commonNames := in.CommonNames; commonNames != nil { + common_names := make([]map[string]interface{}, 0) + for _, i := range *commonNames { + commonName := make(map[string]interface{}) + + if i.CertificateCommonName != nil { + commonName["certificate_common_name"] = *i.CertificateCommonName + } + + if i.CertificateIssuerThumbprint != nil { + commonName["certificate_issuer_thumbprint"] = *i.CertificateIssuerThumbprint + } + + common_names = append(common_names, commonName) + } + + output["common_names"] = common_names + } + + output["x509_store_name"] = string(in.X509StoreName) + + return []interface{}{output} +} + func expandServiceFabricClusterReverseProxyCertificate(input []interface{}) *servicefabric.CertificateDescription { if len(input) == 0 { return nil diff --git a/azurerm/resource_arm_service_fabric_cluster_test.go b/azurerm/resource_arm_service_fabric_cluster_test.go index 401acb037509..1a91609e9910 100644 --- a/azurerm/resource_arm_service_fabric_cluster_test.go +++ b/azurerm/resource_arm_service_fabric_cluster_test.go @@ -45,6 +45,57 @@ func TestAccAzureRMServiceFabricCluster_basic(t *testing.T) { }) } +func TestAccAzureRMServiceFabricCluster_basicNodeTypeUpdate(t *testing.T) { + resourceName := "azurerm_service_fabric_cluster.test" + ri := tf.AccRandTimeInt() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMServiceFabricClusterDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMServiceFabricCluster_basic(ri, testLocation(), 3), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMServiceFabricClusterExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "management_endpoint", "http://example:80"), + resource.TestCheckResourceAttr(resourceName, "add_on_features.#", "0"), + resource.TestCheckResourceAttr(resourceName, "certificate.#", "0"), + resource.TestCheckResourceAttr(resourceName, "reverse_proxy_certificate.#", "0"), + resource.TestCheckResourceAttr(resourceName, "client_certificate_thumbprint.#", "0"), + resource.TestCheckResourceAttr(resourceName, "azure_active_directory.#", "0"), + resource.TestCheckResourceAttr(resourceName, "diagnostics_config.#", "0"), + resource.TestCheckResourceAttr(resourceName, "node_type.#", "1"), + resource.TestCheckResourceAttr(resourceName, "node_type.0.is_primary", "true"), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + ), + }, + { + Config: testAccAzureRMServiceFabricCluster_basicNodeTypeUpdate(ri, testLocation(), 3, 3), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMServiceFabricClusterExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "management_endpoint", "http://example:80"), + resource.TestCheckResourceAttr(resourceName, "add_on_features.#", "0"), + resource.TestCheckResourceAttr(resourceName, "certificate.#", "0"), + resource.TestCheckResourceAttr(resourceName, "reverse_proxy_certificate.#", "0"), + resource.TestCheckResourceAttr(resourceName, "client_certificate_thumbprint.#", "0"), + resource.TestCheckResourceAttr(resourceName, "azure_active_directory.#", "0"), + resource.TestCheckResourceAttr(resourceName, "diagnostics_config.#", "0"), + resource.TestCheckResourceAttr(resourceName, "node_type.#", "2"), + resource.TestCheckResourceAttr(resourceName, "node_type.0.is_primary", "true"), + resource.TestCheckResourceAttr(resourceName, "node_type.1.is_primary", "false"), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func TestAccAzureRMServiceFabricCluster_requiresImport(t *testing.T) { if !requireResourcesToBeImported { t.Skip("Skipping since resources aren't required to be imported") @@ -404,6 +455,35 @@ func TestAccAzureRMServiceFabricCluster_readerAdminClientCertificateThumbprint(t }) } +func TestAccAzureRMServiceFabricCluster_certificateCommonNames(t *testing.T) { + resourceName := "azurerm_service_fabric_cluster.test" + ri := tf.AccRandTimeInt() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMServiceFabricClusterDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMServiceFabricCluster_certificateCommonNames(ri, testLocation()), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMServiceFabricClusterExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "certificate_common_names.0.common_names.2962847220.certificate_common_name", "example"), + resource.TestCheckResourceAttr(resourceName, "certificate_common_names.0.x509_store_name", "My"), + resource.TestCheckResourceAttr(resourceName, "fabric_settings.0.name", "Security"), + resource.TestCheckResourceAttr(resourceName, "fabric_settings.0.parameters.ClusterProtectionLevel", "EncryptAndSign"), + resource.TestCheckResourceAttr(resourceName, "management_endpoint", "https://example:80"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func TestAccAzureRMServiceFabricCluster_azureActiveDirectory(t *testing.T) { resourceName := "azurerm_service_fabric_cluster.test" ri := tf.AccRandTimeInt() @@ -426,7 +506,54 @@ func TestAccAzureRMServiceFabricCluster_azureActiveDirectory(t *testing.T) { resource.TestCheckResourceAttrSet(resourceName, "azure_active_directory.0.client_application_id"), resource.TestCheckResourceAttr(resourceName, "fabric_settings.0.name", "Security"), resource.TestCheckResourceAttr(resourceName, "fabric_settings.0.parameters.ClusterProtectionLevel", "EncryptAndSign"), - resource.TestCheckResourceAttr(resourceName, "management_endpoint", "https://example:80"), + resource.TestCheckResourceAttr(resourceName, "management_endpoint", "https://example:19080"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureRMServiceFabricCluster_azureActiveDirectoryDelete(t *testing.T) { + resourceName := "azurerm_service_fabric_cluster.test" + ri := tf.AccRandTimeInt() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMServiceFabricClusterDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMServiceFabricCluster_azureActiveDirectory(ri, testLocation()), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMServiceFabricClusterExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "certificate.#", "1"), + resource.TestCheckResourceAttr(resourceName, "certificate.0.thumbprint", "33:41:DB:6C:F2:AF:72:C6:11:DF:3B:E3:72:1A:65:3A:F1:D4:3E:CD:50:F5:84:F8:28:79:3D:BE:91:03:C3:EE"), + resource.TestCheckResourceAttr(resourceName, "certificate.0.x509_store_name", "My"), + resource.TestCheckResourceAttr(resourceName, "azure_active_directory.#", "1"), + resource.TestCheckResourceAttrSet(resourceName, "azure_active_directory.0.tenant_id"), + resource.TestCheckResourceAttrSet(resourceName, "azure_active_directory.0.cluster_application_id"), + resource.TestCheckResourceAttrSet(resourceName, "azure_active_directory.0.client_application_id"), + resource.TestCheckResourceAttr(resourceName, "fabric_settings.0.name", "Security"), + resource.TestCheckResourceAttr(resourceName, "fabric_settings.0.parameters.ClusterProtectionLevel", "EncryptAndSign"), + resource.TestCheckResourceAttr(resourceName, "management_endpoint", "https://example:19080"), + ), + }, + { + Config: testAccAzureRMServiceFabricCluster_azureActiveDirectoryDelete(ri, testLocation()), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMServiceFabricClusterExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "certificate.#", "1"), + resource.TestCheckResourceAttr(resourceName, "certificate.0.thumbprint", "33:41:DB:6C:F2:AF:72:C6:11:DF:3B:E3:72:1A:65:3A:F1:D4:3E:CD:50:F5:84:F8:28:79:3D:BE:91:03:C3:EE"), + resource.TestCheckResourceAttr(resourceName, "certificate.0.x509_store_name", "My"), + resource.TestCheckResourceAttr(resourceName, "azure_active_directory.#", "0"), + resource.TestCheckResourceAttr(resourceName, "fabric_settings.0.name", "Security"), + resource.TestCheckResourceAttr(resourceName, "fabric_settings.0.parameters.ClusterProtectionLevel", "EncryptAndSign"), + resource.TestCheckResourceAttr(resourceName, "management_endpoint", "https://example:19080"), ), }, { @@ -469,6 +596,44 @@ func TestAccAzureRMServiceFabricCluster_diagnosticsConfig(t *testing.T) { }) } +func TestAccAzureRMServiceFabricCluster_diagnosticsConfigDelete(t *testing.T) { + resourceName := "azurerm_service_fabric_cluster.test" + ri := tf.AccRandTimeInt() + rs := acctest.RandString(4) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMServiceFabricClusterDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMServiceFabricCluster_diagnosticsConfig(ri, rs, testLocation()), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMServiceFabricClusterExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "diagnostics_config.#", "1"), + resource.TestCheckResourceAttrSet(resourceName, "diagnostics_config.0.storage_account_name"), + resource.TestCheckResourceAttrSet(resourceName, "diagnostics_config.0.protected_account_key_name"), + resource.TestCheckResourceAttrSet(resourceName, "diagnostics_config.0.blob_endpoint"), + resource.TestCheckResourceAttrSet(resourceName, "diagnostics_config.0.queue_endpoint"), + resource.TestCheckResourceAttrSet(resourceName, "diagnostics_config.0.table_endpoint"), + ), + }, + { + Config: testAccAzureRMServiceFabricCluster_diagnosticsConfigDelete(ri, rs, testLocation()), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMServiceFabricClusterExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "diagnostics_config.#", "0"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func TestAccAzureRMServiceFabricCluster_fabricSettings(t *testing.T) { resourceName := "azurerm_service_fabric_cluster.test" ri := tf.AccRandTimeInt() @@ -738,8 +903,8 @@ resource "azurerm_service_fabric_cluster" "test" { reliability_level = "Bronze" upgrade_mode = "Automatic" vm_image = "Windows" - management_endpoint = "http://example:80" - + management_endpoint = "http://example:80" + node_type { name = "first" instance_count = %d @@ -751,6 +916,41 @@ resource "azurerm_service_fabric_cluster" "test" { `, rInt, location, rInt, count) } +func testAccAzureRMServiceFabricCluster_basicNodeTypeUpdate(rInt int, location string, count int, secondary_count int) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_service_fabric_cluster" "test" { + name = "acctest-%d" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" + reliability_level = "Bronze" + upgrade_mode = "Automatic" + vm_image = "Windows" + management_endpoint = "http://example:80" + + node_type { + name = "first" + instance_count = %d + is_primary = true + client_endpoint_port = 2020 + http_endpoint_port = 80 + } + + node_type { + name = "second" + instance_count = %d + is_primary = false + client_endpoint_port = 2020 + http_endpoint_port = 80 + } +} +`, rInt, location, rInt, count, secondary_count) +} + func testAccAzureRMServiceFabricCluster_requiresImport(rInt int, location string, count int) string { return fmt.Sprintf(` %s @@ -762,8 +962,8 @@ resource "azurerm_service_fabric_cluster" "import" { reliability_level = "${azurerm_service_fabric_cluster.test.reliability_level}" upgrade_mode = "${azurerm_service_fabric_cluster.test.upgrade_mode}" vm_image = "${azurerm_service_fabric_cluster.test.vm_image}" - management_endpoint = "${azurerm_service_fabric_cluster.test.management_endpoint}" - + management_endpoint = "${azurerm_service_fabric_cluster.test.management_endpoint}" + node_type { name = "first" instance_count = %d @@ -790,8 +990,8 @@ resource "azurerm_service_fabric_cluster" "test" { upgrade_mode = "Manual" cluster_code_version = "%[3]s" vm_image = "Windows" - management_endpoint = "http://example:80" - + management_endpoint = "http://example:80" + node_type { name = "first" instance_count = 3 @@ -818,8 +1018,8 @@ resource "azurerm_service_fabric_cluster" "test" { upgrade_mode = "Automatic" vm_image = "Windows" management_endpoint = "http://example:80" - add_on_features = ["DnsService", "RepairManager"] - + add_on_features = ["DnsService", "RepairManager"] + node_type { name = "first" instance_count = 3 @@ -845,21 +1045,21 @@ resource "azurerm_service_fabric_cluster" "test" { reliability_level = "Bronze" upgrade_mode = "Automatic" vm_image = "Windows" - management_endpoint = "https://example:80" - + management_endpoint = "https://example:80" + certificate { thumbprint = "33:41:DB:6C:F2:AF:72:C6:11:DF:3B:E3:72:1A:65:3A:F1:D4:3E:CD:50:F5:84:F8:28:79:3D:BE:91:03:C3:EE" x509_store_name = "My" - } - + } + fabric_settings { - name = "Security" - + name = "Security" + parameters = { "ClusterProtectionLevel" = "EncryptAndSign" } - } - + } + node_type { name = "first" instance_count = 3 @@ -885,26 +1085,26 @@ resource "azurerm_service_fabric_cluster" "test" { reliability_level = "Bronze" upgrade_mode = "Automatic" vm_image = "Windows" - management_endpoint = "https://example:80" - + management_endpoint = "https://example:80" + certificate { thumbprint = "33:41:DB:6C:F2:AF:72:C6:11:DF:3B:E3:72:1A:65:3A:F1:D4:3E:CD:50:F5:84:F8:28:79:3D:BE:91:03:C3:EE" x509_store_name = "My" - } - + } + reverse_proxy_certificate { thumbprint = "33:41:DB:6C:F2:AF:72:C6:11:DF:3B:E3:72:1A:65:3A:F1:D4:3E:CD:50:F5:84:F8:28:79:3D:BE:91:03:C3:EE" x509_store_name = "My" - } - + } + fabric_settings { - name = "Security" - + name = "Security" + parameters = { "ClusterProtectionLevel" = "EncryptAndSign" } - } - + } + node_type { name = "first" instance_count = 3 @@ -931,26 +1131,26 @@ resource "azurerm_service_fabric_cluster" "test" { reliability_level = "Bronze" upgrade_mode = "Automatic" vm_image = "Windows" - management_endpoint = "https://example:80" - + management_endpoint = "https://example:80" + certificate { thumbprint = "33:41:DB:6C:F2:AF:72:C6:11:DF:3B:E3:72:1A:65:3A:F1:D4:3E:CD:50:F5:84:F8:28:79:3D:BE:91:03:C3:EE" x509_store_name = "My" - } - + } + client_certificate_thumbprint { thumbprint = "33:41:DB:6C:F2:AF:72:C6:11:DF:3B:E3:72:1A:65:3A:F1:D4:3E:CD:50:F5:84:F8:28:79:3D:BE:91:03:C3:EE" is_admin = true - } - + } + fabric_settings { - name = "Security" - + name = "Security" + parameters = { "ClusterProtectionLevel" = "EncryptAndSign" } - } - + } + node_type { name = "first" instance_count = 3 @@ -976,31 +1176,74 @@ resource "azurerm_service_fabric_cluster" "test" { reliability_level = "Bronze" upgrade_mode = "Automatic" vm_image = "Windows" - management_endpoint = "https://example:80" - + management_endpoint = "https://example:80" + certificate { thumbprint = "33:41:DB:6C:F2:AF:72:C6:11:DF:3B:E3:72:1A:65:3A:F1:D4:3E:CD:50:F5:84:F8:28:79:3D:BE:91:03:C3:EE" x509_store_name = "My" - } - + } + client_certificate_thumbprint { thumbprint = "33:41:DB:6C:F2:AF:72:C6:11:DF:3B:E3:72:1A:65:3A:F1:D4:3E:CD:50:F5:84:F8:28:79:3D:BE:91:03:C3:EE" is_admin = true - } - + } + client_certificate_thumbprint { thumbprint = "33:41:DB:6C:F2:AF:72:C6:11:DF:3B:E3:72:1A:65:3A:F1:D4:3E:CD:50:F5:84:F8:28:79:3D:BE:91:03:C3:EE" is_admin = false - } - + } + fabric_settings { - name = "Security" - + name = "Security" + parameters = { "ClusterProtectionLevel" = "EncryptAndSign" } - } - + } + + node_type { + name = "first" + instance_count = 3 + is_primary = true + client_endpoint_port = 2020 + http_endpoint_port = 80 + } +} +`, rInt, location, rInt) +} + +func testAccAzureRMServiceFabricCluster_certificateCommonNames(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_service_fabric_cluster" "test" { + name = "acctest-%d" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" + reliability_level = "Bronze" + upgrade_mode = "Automatic" + vm_image = "Windows" + management_endpoint = "https://example:80" + + certificate_common_names { + common_names { + certificate_common_name = "example" + } + + x509_store_name = "My" + } + + fabric_settings { + name = "Security" + + parameters = { + "ClusterProtectionLevel" = "EncryptAndSign" + } + } + node_type { name = "first" instance_count = 3 @@ -1021,13 +1264,60 @@ resource "azurerm_resource_group" "test" { data "azurerm_client_config" "current" {} -resource "azuread_application" "test" { - name = "${azurerm_resource_group.test.name}-AAD" - homepage = "https://example:80/Explorer/index.html" - identifier_uris = ["https://acctestAAD-app"] - reply_urls = ["https://acctestAAD-app"] +resource "azuread_application" "cluster_explorer" { + name = "${azurerm_resource_group.test.name}-explorer-AAD" + homepage = "https://example:19080/Explorer/index.html" + identifier_uris = ["https://example:19080/Explorer/index.html"] + reply_urls = ["https://example:19080/Explorer/index.html"] available_to_other_tenants = false oauth2_allow_implicit_flow = true + + # https://blogs.msdn.microsoft.com/aaddevsup/2018/06/06/guid-table-for-windows-azure-active-directory-permissions/ + # https://shawntabrizi.com/aad/common-microsoft-resources-azure-active-directory/ + required_resource_access { + resource_app_id = "00000002-0000-0000-c000-000000000000" + + resource_access { + id = "311a71cc-e848-46a1-bdf8-97ff7156d8e6" + type = "Scope" + } + } +} + +resource "azuread_service_principal" "cluster_explorer" { + application_id = "${azuread_application.cluster_explorer.application_id}" +} + +resource "azuread_application" "cluster_console" { + name = "${azurerm_resource_group.test.name}-console-AAD" + type = "native" + reply_urls = ["urn:ietf:wg:oauth:2.0:oob"] + available_to_other_tenants = false + oauth2_allow_implicit_flow = true + + # https://blogs.msdn.microsoft.com/aaddevsup/2018/06/06/guid-table-for-windows-azure-active-directory-permissions/ + # https://shawntabrizi.com/aad/common-microsoft-resources-azure-active-directory/ + required_resource_access { + resource_app_id = "00000002-0000-0000-c000-000000000000" + + resource_access { + id = "311a71cc-e848-46a1-bdf8-97ff7156d8e6" + type = "Scope" + } + } + + required_resource_access { + resource_app_id = "${azuread_application.cluster_explorer.application_id}" + + resource_access { + id = "${azuread_application.cluster_explorer.oauth2_permissions.0.id}" + type = "Scope" + } + } +} + +resource "azuread_service_principal" "cluster_console" { + application_id = "${azuread_application.cluster_console.application_id}" } resource "azurerm_service_fabric_cluster" "test" { @@ -1037,33 +1327,75 @@ resource "azurerm_service_fabric_cluster" "test" { reliability_level = "Bronze" upgrade_mode = "Automatic" vm_image = "Windows" - management_endpoint = "https://example:80" - + management_endpoint = "https://example:19080" + certificate { thumbprint = "33:41:DB:6C:F2:AF:72:C6:11:DF:3B:E3:72:1A:65:3A:F1:D4:3E:CD:50:F5:84:F8:28:79:3D:BE:91:03:C3:EE" x509_store_name = "My" - } - + } + azure_active_directory { tenant_id = "${data.azurerm_client_config.current.tenant_id}" - cluster_application_id = "${azuread_application.test.application_id}" - client_application_id = "00000000-0000-0000-0000-000000000000" - } - + cluster_application_id = "${azuread_application.cluster_explorer.application_id}" + client_application_id = "${azuread_application.cluster_console.application_id}" + } + fabric_settings { - name = "Security" - + name = "Security" + parameters = { "ClusterProtectionLevel" = "EncryptAndSign" } - } - + } + node_type { - name = "first" + name = "system" instance_count = 3 is_primary = true - client_endpoint_port = 2020 - http_endpoint_port = 80 + client_endpoint_port = 19000 + http_endpoint_port = 19080 + } +} +`, rInt, location, rInt) +} + +func testAccAzureRMServiceFabricCluster_azureActiveDirectoryDelete(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +data "azurerm_client_config" "current" {} + +resource "azurerm_service_fabric_cluster" "test" { + name = "acctest-%d" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" + reliability_level = "Bronze" + upgrade_mode = "Automatic" + vm_image = "Windows" + management_endpoint = "https://example:19080" + + certificate { + thumbprint = "33:41:DB:6C:F2:AF:72:C6:11:DF:3B:E3:72:1A:65:3A:F1:D4:3E:CD:50:F5:84:F8:28:79:3D:BE:91:03:C3:EE" + x509_store_name = "My" + } + + fabric_settings { + name = "Security" + + parameters = { + "ClusterProtectionLevel" = "EncryptAndSign" + } + } + + node_type { + name = "system" + instance_count = 3 + is_primary = true + client_endpoint_port = 19000 + http_endpoint_port = 19080 } } `, rInt, location, rInt) @@ -1091,16 +1423,51 @@ resource "azurerm_service_fabric_cluster" "test" { reliability_level = "Bronze" upgrade_mode = "Automatic" vm_image = "Windows" - management_endpoint = "http://example:80" - + management_endpoint = "http://example:80" + diagnostics_config { storage_account_name = "${azurerm_storage_account.test.name}" protected_account_key_name = "StorageAccountKey1" blob_endpoint = "${azurerm_storage_account.test.primary_blob_endpoint}" queue_endpoint = "${azurerm_storage_account.test.primary_queue_endpoint}" table_endpoint = "${azurerm_storage_account.test.primary_table_endpoint}" - } - + } + + node_type { + name = "first" + instance_count = 3 + is_primary = true + client_endpoint_port = 2020 + http_endpoint_port = 80 + } +} +`, rInt, location, rString, rInt) +} + +func testAccAzureRMServiceFabricCluster_diagnosticsConfigDelete(rInt int, rString, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_storage_account" "test" { + name = "acctestsa%s" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" + account_tier = "Standard" + account_replication_type = "GRS" +} + +resource "azurerm_service_fabric_cluster" "test" { + name = "acctest-%d" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" + reliability_level = "Bronze" + upgrade_mode = "Automatic" + vm_image = "Windows" + management_endpoint = "http://example:80" + node_type { name = "first" instance_count = 3 @@ -1126,16 +1493,16 @@ resource "azurerm_service_fabric_cluster" "test" { reliability_level = "Bronze" upgrade_mode = "Automatic" vm_image = "Windows" - management_endpoint = "http://example:80" - + management_endpoint = "http://example:80" + fabric_settings { - name = "Security" - + name = "Security" + parameters = { "ClusterProtectionLevel" = "None" } - } - + } + node_type { name = "first" instance_count = 3 @@ -1161,20 +1528,20 @@ resource "azurerm_service_fabric_cluster" "test" { reliability_level = "Bronze" upgrade_mode = "Automatic" vm_image = "Windows" - management_endpoint = "http://example:80" - + management_endpoint = "http://example:80" + node_type { name = "first" instance_count = 3 is_primary = true client_endpoint_port = 2020 - http_endpoint_port = 80 - + http_endpoint_port = 80 + application_ports { start_port = 20000 end_port = 29999 - } - + } + ephemeral_ports { start_port = 30000 end_port = 39999 @@ -1198,16 +1565,16 @@ resource "azurerm_service_fabric_cluster" "test" { reliability_level = "Bronze" upgrade_mode = "Automatic" vm_image = "Windows" - management_endpoint = "http://example:80" - + management_endpoint = "http://example:80" + node_type { name = "first" instance_count = 3 is_primary = true client_endpoint_port = 2020 http_endpoint_port = 80 - } - + } + node_type { name = "second" instance_count = 4 @@ -1234,18 +1601,18 @@ resource "azurerm_service_fabric_cluster" "test" { vm_image = "Windows" management_endpoint = "http://example:80" node_type { - name = "first" - placement_properties = { - "HasSSD" = "true" - } - capacities = { - "ClientConnections" = "20000" - "MemoryGB" = "8" - } + name = "first" + placement_properties = { + "HasSSD" = "true" + } + capacities = { + "ClientConnections" = "20000" + "MemoryGB" = "8" + } instance_count = 3 is_primary = true client_endpoint_port = 2020 - http_endpoint_port = 80 + http_endpoint_port = 80 } tags = { Hello = "World" @@ -1268,16 +1635,16 @@ resource "azurerm_service_fabric_cluster" "test" { reliability_level = "Bronze" upgrade_mode = "Automatic" vm_image = "Windows" - management_endpoint = "http://example:80" - + management_endpoint = "http://example:80" + node_type { name = "first" instance_count = 3 is_primary = true client_endpoint_port = 2020 http_endpoint_port = 80 - } - + } + tags = { Hello = "World" } diff --git a/website/docs/r/service_fabric_cluster.html.markdown b/website/docs/r/service_fabric_cluster.html.markdown index 55c96f0f907f..d31a5770925d 100644 --- a/website/docs/r/service_fabric_cluster.html.markdown +++ b/website/docs/r/service_fabric_cluster.html.markdown @@ -67,7 +67,9 @@ The following arguments are supported: * `azure_active_directory` - (Optional) An `azure_active_directory` block as defined below. -* `certificate` - (Optional) A `certificate` block as defined below. +* `certificate_common_names` - (Optional) A `certificate_common_names` block as defined below. Conflicts with `certificate`. + +* `certificate` - (Optional) A `certificate` block as defined below. Conflicts with `certificate_common_names`. * `reverse_proxy_certificate` - (Optional) A `reverse_proxy_certificate` block as defined below. @@ -93,6 +95,24 @@ A `azure_active_directory` block supports the following: --- +A `certificate_common_names` block supports the following: + +* `common_names` - (Required) A `common_names` block as defined below. + +* `x509_store_name` - (Required) The X509 Store where the Certificate Exists, such as `My`. + +--- + +A `common_names` block supports the following: + +* `certificate_common_name` - (Required) The common or subject name of the certificate. + +* `certificate_issuer_thumbprint` - (Optional) The Issuer Thumbprint of the Certificate. + +-> **NOTE:** Certificate Issuer Thumbprint may become required in the future, `https://docs.microsoft.com/en-us/azure/service-fabric/service-fabric-create-cluster-using-cert-cn#download-and-update-a-sample-template`. + +--- + A `certificate` block supports the following: * `thumbprint` - (Required) The Thumbprint of the Certificate.