azurerm_function_app_flex_consumption - add function app backend storage option#29099
azurerm_function_app_flex_consumption - add function app backend storage option#29099xiaxyi wants to merge 6 commits intohashicorp:mainfrom
azurerm_function_app_flex_consumption - add function app backend storage option#29099Conversation
|
@stephybun in case you can help review this PR. We have a policy for all our samples for Flex Consumption to use managed identity and I am waiting for this PR to get reviewed before updating them. |
|
@rcskosir appreciate any help getting this one moving. We need to configure managed indentity in all our samples so waiting for this to complete before updating them on https://aka.ms/flexconsumption/samples |
katbyte
left a comment
There was a problem hiding this comment.
Looks like all tests are not failing with
------- Stdout: -------
=== RUN TestAccFunctionAppFlexConsumption_appSettings
=== PAUSE TestAccFunctionAppFlexConsumption_appSettings
=== CONT TestAccFunctionAppFlexConsumption_appSettings
testcase.go:173: Step 1/3 error: After applying this test step, the non-refresh plan was not empty.
stdout:
Terraform used the selected providers to generate the following execution
plan. Resource actions are indicated with the following symbols:
~ update in-place
Terraform will perform the following actions:
# azurerm_application_insights.test will be updated in-place
~ resource "azurerm_application_insights" "test" {
id = "/subscriptions/*******/resourceGroups/acctestRG-LFA-250421185620141720/providers/Microsoft.Insights/components/acctestappinsights-250421185620141720"
name = "acctestappinsights-250421185620141720"
- workspace_id = "/subscriptions/*******/resourceGroups/ai_acctestappinsights-250421185620141720_20fdd8c2-be14-42fa-9625-31aa0b60e41b_managed/providers/Microsoft.OperationalInsights/workspaces/managed-acctestappinsights-250421185620141720-ws" -> null
# (15 unchanged attributes hidden)
}
Plan: 0 to add, 1 to change, 0 to destroy.
--- FAIL: TestAccFunctionAppFlexConsumption_appSettings (238.55s)
FAIL
due to i presume a service change, this will need to be fixed before this pr can be merged
|
@katbyte The failure was due to api update from the app insight side, I updated the test cases. |
|
@katbyte could you please take a look again? |
catriona-m
left a comment
There was a problem hiding this comment.
Hi @xiaxyi thanks for your work on this PR. I had a look through and suggested a couple of changes, but once those are addressed I can take another look. Thanks!
| Description: "The ID of the App Service Plan within which to create this Function App", | ||
| }, | ||
|
|
||
| "function_app_storage_account_name": { |
There was a problem hiding this comment.
should we remove function_app prefix from this property as this should be evident from the resource name?
| "function_app_storage_account_name": { | |
| "storage_account_name": { |
There was a problem hiding this comment.
There are two storage accounts that can be configured - one for the function app's own workings - translates to AzureWebJobsStorage app setting(s) - and there's a separate configuration for the storage account and container for the deployment where the customer zip file would be placed. They can be the same or they can be different.
@xiaxyi - my recommendation would be:
storage_account_name, storage_account_access_key, storage_uses_managed_identity, storage_key_vault_secret_id, storage_user_assigned_identity_id (as recommended by @catriona-m ) - would apply to the first one related to AzureWebJobsStorage.
Then we rename the deployment ones to include deployment_ prefix:
deployment_storage_container_type, deployment_storage_container_endpoint, deployment_storage_authentication_type, deployment_storage_access_key, deployment_storage_user_assigned_identity_id (note this last one doesn't seem to exist already @xiaxyi , we need it as the user identity for deployment can be different from the main function identity given you can assign multiple identities to the same app)
There was a problem hiding this comment.
@nzthiago Thanks for the comment, your suggestions totally make sense. However, I'm afraid that we can't change the naming of an existing property because it will cause breaking changes for existing users, unless in a major release. can we @catriona-m ?
There was a problem hiding this comment.
@catriona-m , Thanks for the comment, as @nzthiago mentioned, there are two types of storage account that can be configured for the flex consumption app; One is the storage account that will be accessed by the function app for the runtime related tasks, the other is the deployment storage account that holds user's function app's deployment related tasks. These two storage accounts can be different, so we need to differentiate them. I think what @nzthiago suggested may be the way, but we can't change the existing name due to breaking changes, so I'm suggesting, we stick to the current naming and I will add the 6.0 flag to these two storage account name, WDYT?
| }, | ||
| }, | ||
|
|
||
| "function_app_storage_account_access_key": { |
There was a problem hiding this comment.
| "function_app_storage_account_access_key": { | |
| "storage_account_access_key": { |
| Description: "The access key which will be used to access the storage account for the Function App.", | ||
| }, | ||
|
|
||
| "function_app_storage_uses_managed_identity": { |
There was a problem hiding this comment.
| "function_app_storage_uses_managed_identity": { | |
| "storage_uses_managed_identity": { |
| Description: "Should the Function App use its Managed Identity to access storage?", | ||
| }, | ||
|
|
||
| "function_app_storage_key_vault_secret_id": { |
There was a problem hiding this comment.
| "function_app_storage_key_vault_secret_id": { | |
| "storage_key_vault_secret_id": { |
| "key_vault_reference_identity_id": { | ||
| Type: pluginsdk.TypeString, | ||
| Optional: true, | ||
| Computed: true, |
There was a problem hiding this comment.
if a computed value is returned for this property, could we leave a comment explaining why this is computed using // NOTE: O+C or remove Computed: true, if this is not necessary
There was a problem hiding this comment.
it looks like this comment wasn't addressed?
| storage_container_endpoint = "${azurerm_storage_account.test.primary_blob_endpoint}${azurerm_storage_container.test.name}" | ||
| storage_authentication_type = "StorageAccountConnectionString" | ||
| storage_access_key = azurerm_storage_account.test.primary_access_key | ||
| storage_access_key = azurerm_storage_account.test.primary_connection_string |
There was a problem hiding this comment.
is there a reason for changing these in the existing tests?
There was a problem hiding this comment.
it looks like this comment wasn't addressed?
There was a problem hiding this comment.
@catriona-m for this one, the actually value being used is connection string not the access key. I will make another PR to address the name
|
@catriona-m my response is added inline, let me know your thoughts |
catriona-m
left a comment
There was a problem hiding this comment.
thanks @xiaxyi - it looks like a couple of my original comments got missed if you could take another look? Thanks!
| "key_vault_reference_identity_id": { | ||
| Type: pluginsdk.TypeString, | ||
| Optional: true, | ||
| Computed: true, |
There was a problem hiding this comment.
it looks like this comment wasn't addressed?
| storage_container_endpoint = "${azurerm_storage_account.test.primary_blob_endpoint}${azurerm_storage_container.test.name}" | ||
| storage_authentication_type = "StorageAccountConnectionString" | ||
| storage_access_key = azurerm_storage_account.test.primary_access_key | ||
| storage_access_key = azurerm_storage_account.test.primary_connection_string |
There was a problem hiding this comment.
it looks like this comment wasn't addressed?
|
@catriona-m Thanks for the comment, I've modified the storage account name per your suggestion and I will make another PR to address the name better. --- PASS: TestAccFunctionAppFlexConsumption_backendStorageUsingKeyValutString (1239.22s) |
| functionAppStorageAccountString := functionAppFlexConsumption.StorageAccountName | ||
| if !functionAppFlexConsumption.StorageAccountUsesMSI { | ||
| if functionAppFlexConsumption.StorageAccountKeyVaultSecretID != "" { | ||
| functionAppStorageAccountString = fmt.Sprintf(helpers.StorageStringFmtKV, functionAppFlexConsumption.StorageAccountKeyVaultSecretID) | ||
| } else if functionAppStorageAccountString != "" { | ||
| functionAppStorageAccountString = fmt.Sprintf(helpers.StorageStringFmt, functionAppFlexConsumption.StorageAccountName, functionAppFlexConsumption.StorageAccountAccessKey, *storageDomainSuffix) | ||
| } | ||
| } |
There was a problem hiding this comment.
This may be more readable / maintainable as a switch statement.
There was a problem hiding this comment.
Thanks @wyattfry for the comment, Would you mind be more specific about which variable to put in the switch condition? This code is calculating the storage string based on the other two properties, just like azurerm_linux_function_app did.
| Type: &storageAuthType, | ||
| } | ||
|
|
||
| endpoint := strings.TrimPrefix(deploymentSaEndpoint, "https://") |
There was a problem hiding this comment.
The manual string parsing I feel would be very easy to break. What if we used "net/url"? Something maybe like this:
u, err := url.Parse(deploymentSaEndpoint)
if err != nil {
return (err)
}
storageName := strings.Split(u.Host, ".")[0]
You could also move the parsing implementation to the package's parsing sub-package. Then you could add validation and unit tests to make sure it works as expected.
| } | ||
|
|
||
| endpoint := strings.TrimPrefix(deploymentSaEndpoint, "https://") | ||
| var deploymentSaConncString string |
There was a problem hiding this comment.
The name seems a little unconventional, maybe this?
| var deploymentSaConncString string | |
| var deploymentSaConnString string |
|
|
||
| if functionAppFlexConsumption.StorageAuthType == string(webapps.AuthenticationTypeStorageAccountConnectionString) { | ||
| if functionAppFlexConsumption.StorageAccessKey == "" { | ||
| if storageAuthType == webapps.AuthenticationTypeStorageAccountConnectionString { |
There was a problem hiding this comment.
For these two checks, I feel like a CustomizeDiff() would be a better place for them to live.
There was a problem hiding this comment.
Thanks @wyattfry for the comment, the storage_authentication_type is defined as computed, can we still define the checks in CustomizeDiff ()?
| appSettingsResp, err := client.ListApplicationSettings(ctx, *id) | ||
| if err != nil { | ||
| return fmt.Errorf("retrieving App Settings for %s: %+v", id, err) | ||
| } |
There was a problem hiding this comment.
Could this be moved closer to where it's used? Around line 1221?
| storageConnStringForFCApp := "" | ||
| storageConnStringForFcAppValue := "" | ||
| var deploymentSaName, deploymentSaKey, backendStorageString string |
There was a problem hiding this comment.
You could condense / neaten even further...
| storageConnStringForFCApp := "" | |
| storageConnStringForFcAppValue := "" | |
| var deploymentSaName, deploymentSaKey, backendStorageString string | |
| var ( | |
| storageConnStringForFCApp, | |
| storageConnStringForFcAppValue, | |
| deploymentSaName, | |
| deploymentSaKey, | |
| backendStorageString string | |
| ) |
| deploymentSaKey = state.DeploymentStorageAccessKey | ||
| } | ||
|
|
||
| endpoint := strings.TrimPrefix(deploymentSaEndpoint, "https://") |
There was a problem hiding this comment.
See above about URL parsing.
| } | ||
| if state.StorageAuthType == string(webapps.AuthenticationTypeStorageAccountConnectionString) { | ||
| if state.StorageAccessKey == "" { | ||
| if storageAuthType == webapps.AuthenticationTypeStorageAccountConnectionString { |
There was a problem hiding this comment.
See note above about CustomizeDiff
| if StorageUserAssignedIdentityID == "" { | ||
| return fmt.Errorf("the user assigned identity id must be specified when using the user assigned identity to access the storage account") | ||
| } | ||
| storageAuth.UserAssignedIdentityResourceId = &StorageUserAssignedIdentityID |
There was a problem hiding this comment.
I know you declare this variable a couple lines above, but for safety, it may be worth using pointer.To
| storageAuth.UserAssignedIdentityResourceId = &StorageUserAssignedIdentityID | |
| storageAuth.UserAssignedIdentityResourceId = pointer.To(StorageUserAssignedIdentityID) |
| storageAuth := webapps.FunctionsDeploymentStorageAuthentication{ | ||
| Type: &storageAuthType, | ||
| } | ||
| if state.DeploymentStorageAuthType == string(webapps.AuthenticationTypeStorageAccountConnectionString) { |
There was a problem hiding this comment.
See note above about CustomizeDiff
|
|
||
| // Note: We process this regardless to give us a "clean" view of service-side app_settings, so we can reconcile the user-defined entries later | ||
| siteConfig, err := helpers.ExpandSiteConfigFunctionFlexConsumptionApp(state.SiteConfig, model.Properties.SiteConfig, metadata, false, storageString, storageConnStringForFCApp) | ||
| siteConfig, err := helpers.ExpandSiteConfigFunctionFlexConsumptionApp(state.SiteConfig, model.Properties.SiteConfig, metadata, state.StorageAccountUsesMSI, backendStorageString, storageConnStringForFCApp, storageConnStringForFcAppValue) |
There was a problem hiding this comment.
I thought it was a nice move when previously you put each argument on its own line, maybe here too?
There was a problem hiding this comment.
Thanks @wyattfry for this comment, would you mind be more specific about it?
wyattfry
left a comment
There was a problem hiding this comment.
Thanks for the PR! I left some notes for you to consider. About the acceptance tests, I had a couple suggestions:
- you can remove the
check.That(...)s other than theExistsInAzure(r), the test runner automatically detects differences between the actual resource and the configuration. - rather than writing two functions for every test condition, one for the beta, one not; a pattern I like to use is to have only one test function, but in the configuration function, have it return either version depending on the flag.
Example: https://github.com/hashicorp/terraform-provider-azurerm/blob/main/internal/services/loganalytics/log_analytics_workspace_resource_test.go#L802
|
Thanks @wyattfry for the comment! The concern of remodeling the test cases as what you suggested would be the |
catriona-m
left a comment
There was a problem hiding this comment.
Hi @xiaxyi it looks like there are some comments from a previous review still waiting to be addressed here and some conflicts to be resolved. Once those are fixed up, I can take another look, thanks!
|
Thanks @wyattfry for the review! I updated the code and some of the comments, would you mind taking another look and let me know your suggestions? |
sreallymatt
left a comment
There was a problem hiding this comment.
Hi @xiaxyi, before I give this a more thorough review, I had a question/suggestion regarding the schema. Based on the description and documentation, there are 2 sets of storage related properties, one for backend storage and another for deployment storage.
Instead of having many top level properties, this may be easier to use if there are 2 storage blocks and the properties are contained within, e.g. deployment_storage and backend_storage, WDYT?
|
Thanks @sreallymatt , I think your suggestion totally makes sense. Actually, this was my first thought, however, during my previous practices, I was suggested to use the top-level for this kind of propertice. Would you like me to change to use the block? |
|
Hi @xiaxyi - apologies for the delayed response, I missed this mention. From a UX perspective, having a logical grouping for the properties by using blocks is a good approach, the main difficulty I can see is the fact that we currently have top-level properties for part of it, so this may need a DiffSuppressFunc or O+C for 4.x on some of those, and the top-level args can be deprecated and removed in 5.0. |
|
thanks @sreallymatt , I remodelled the schema and now there are two storage blocks |
wuxu92
left a comment
There was a problem hiding this comment.
Thanks for the update! I left some comments once these are addressed we can have another look.
| "tags": commonschema.Tags(), | ||
| } | ||
| if !features.FivePointOh() { | ||
| schema["backend_storage"] = &pluginsdk.Schema{ |
There was a problem hiding this comment.
backend_storage is the new feature to add, we don't have to mark it as Computed in 4.x, rigth?
There was a problem hiding this comment.
It's a new feature to add, but we still need to make it as computed because it's calculated from AzureWebJobStorage.
| }, | ||
| }, | ||
| }, | ||
| } |
| "container_endpoint": { | ||
| Type: pluginsdk.TypeString, | ||
| Required: true, | ||
| Description: "The endpoint of the storage container where the function app's code is hosted.", |
There was a problem hiding this comment.
add a validation?
| Description: "The endpoint of the storage container where the function app's code is hosted.", | |
| Description: "The endpoint of the storage container where the function app's code is hosted.", | |
| ValidateFunc: validation.StringIsNotEmpty, |
| schema["deployment_storage"] = &pluginsdk.Schema{ | ||
| Type: pluginsdk.TypeList, | ||
| Optional: true, | ||
| Computed: true, | ||
| MaxItems: 1, | ||
| Elem: &pluginsdk.Resource{ | ||
| Schema: map[string]*pluginsdk.Schema{ | ||
| "container_type": { | ||
| Type: pluginsdk.TypeString, | ||
| Required: true, | ||
| ValidateFunc: validation.StringInSlice([]string{ | ||
| string(webapps.FunctionsDeploymentStorageTypeBlobContainer), | ||
| }, false), | ||
| Description: "The type of the storage container where the function app's code is hosted. Only `blobContainer` is supported currently.", | ||
| }, | ||
|
|
||
| "container_endpoint": { | ||
| Type: pluginsdk.TypeString, | ||
| Required: true, | ||
| Description: "The endpoint of the storage container where the function app's code is hosted.", | ||
| }, | ||
|
|
||
| "authentication_type": { | ||
| Type: pluginsdk.TypeString, | ||
| Required: true, | ||
| ValidateFunc: validation.StringInSlice([]string{ | ||
| string(webapps.AuthenticationTypeSystemAssignedIdentity), | ||
| string(webapps.AuthenticationTypeStorageAccountConnectionString), | ||
| string(webapps.AuthenticationTypeUserAssignedIdentity), | ||
| }, false), | ||
| }, | ||
|
|
||
| "access_key": { | ||
| Type: pluginsdk.TypeString, | ||
| Optional: true, | ||
| Sensitive: true, | ||
| ValidateFunc: validation.StringIsNotEmpty, | ||
| }, | ||
|
|
||
| "user_assigned_identity_id": { | ||
| Type: pluginsdk.TypeString, | ||
| Optional: true, | ||
| ValidateFunc: commonids.ValidateUserAssignedIdentityID, | ||
| }, | ||
| }, | ||
| }, | ||
| ExactlyOneOf: []string{"storage_container_type", "deployment_storage"}, | ||
| } |
There was a problem hiding this comment.
why not directly set Computed:
| schema["deployment_storage"] = &pluginsdk.Schema{ | |
| Type: pluginsdk.TypeList, | |
| Optional: true, | |
| Computed: true, | |
| MaxItems: 1, | |
| Elem: &pluginsdk.Resource{ | |
| Schema: map[string]*pluginsdk.Schema{ | |
| "container_type": { | |
| Type: pluginsdk.TypeString, | |
| Required: true, | |
| ValidateFunc: validation.StringInSlice([]string{ | |
| string(webapps.FunctionsDeploymentStorageTypeBlobContainer), | |
| }, false), | |
| Description: "The type of the storage container where the function app's code is hosted. Only `blobContainer` is supported currently.", | |
| }, | |
| "container_endpoint": { | |
| Type: pluginsdk.TypeString, | |
| Required: true, | |
| Description: "The endpoint of the storage container where the function app's code is hosted.", | |
| }, | |
| "authentication_type": { | |
| Type: pluginsdk.TypeString, | |
| Required: true, | |
| ValidateFunc: validation.StringInSlice([]string{ | |
| string(webapps.AuthenticationTypeSystemAssignedIdentity), | |
| string(webapps.AuthenticationTypeStorageAccountConnectionString), | |
| string(webapps.AuthenticationTypeUserAssignedIdentity), | |
| }, false), | |
| }, | |
| "access_key": { | |
| Type: pluginsdk.TypeString, | |
| Optional: true, | |
| Sensitive: true, | |
| ValidateFunc: validation.StringIsNotEmpty, | |
| }, | |
| "user_assigned_identity_id": { | |
| Type: pluginsdk.TypeString, | |
| Optional: true, | |
| ValidateFunc: commonids.ValidateUserAssignedIdentityID, | |
| }, | |
| }, | |
| }, | |
| ExactlyOneOf: []string{"storage_container_type", "deployment_storage"}, | |
| } | |
| schema["deployment_storage"].Computed = true | |
| schema["deployment_storage"].Required = false | |
| schema["deployment_storage"].Optional = true | |
| schema["deployment_storage"].ExactlyOneOf = []string{"storage_container_type", "deployment_storage"} |
| endpoint, err := url.Parse(v.ContainerEndPoint) | ||
| if err != nil { | ||
| return nil, deploymentSaConnectionStr, fmt.Errorf("parsing storage container endpoint error, the expected format is https://storagename.blob.core.windows.net/containername, the received value is %s", v.ContainerEndPoint) | ||
| } |
There was a problem hiding this comment.
if ContainerEndpoint has to be an url, we need to update the Schema.ValidateFunc rather than validate in the exapnd function. so we don't need the err return value in this method.
| deploymentStorageList := make([]DeploymentStorage, 0) | ||
| if deploymentSa == nil { | ||
| return deploymentStorageList | ||
| } |
There was a problem hiding this comment.
| deploymentStorageList := make([]DeploymentStorage, 0) | |
| if deploymentSa == nil { | |
| return deploymentStorageList | |
| } | |
| if deploymentSa == nil { | |
| return []DeploymentStorage{} | |
| } |
| if deploymentSa.Authentication != nil && deploymentSa.Authentication.Type != nil { | ||
| ds.AuthenticationType = string(pointer.From(deploymentSa.Authentication.Type)) | ||
| if deploymentSa.Authentication.Type != nil && pointer.From(deploymentSa.Authentication.Type) == webapps.AuthenticationTypeStorageAccountConnectionString { | ||
| _, ds.AccessKey = helpers.ParseWebJobsStorageString(deploymentSaString) | ||
| } | ||
| if deploymentSa.Authentication != nil && deploymentSa.Authentication.UserAssignedIdentityResourceId != nil { | ||
| ds.UserAssignedIdentityId = pointer.From(deploymentSa.Authentication.UserAssignedIdentityResourceId) | ||
| } | ||
| } |
There was a problem hiding this comment.
nil check for deploymentSa.Authentication is done in the first if condition, we can make this simple:
| if deploymentSa.Authentication != nil && deploymentSa.Authentication.Type != nil { | |
| ds.AuthenticationType = string(pointer.From(deploymentSa.Authentication.Type)) | |
| if deploymentSa.Authentication.Type != nil && pointer.From(deploymentSa.Authentication.Type) == webapps.AuthenticationTypeStorageAccountConnectionString { | |
| _, ds.AccessKey = helpers.ParseWebJobsStorageString(deploymentSaString) | |
| } | |
| if deploymentSa.Authentication != nil && deploymentSa.Authentication.UserAssignedIdentityResourceId != nil { | |
| ds.UserAssignedIdentityId = pointer.From(deploymentSa.Authentication.UserAssignedIdentityResourceId) | |
| } | |
| } | |
| if deploymentSa.Authentication != nil { | |
| ds.AuthenticationType = string(pointer.From(deploymentSa.Authentication.Type)) | |
| if pointer.From(deploymentSa.Authentication.Type) == webapps.AuthenticationTypeStorageAccountConnectionString { | |
| _, ds.AccessKey = helpers.ParseWebJobsStorageString(deploymentSaString) | |
| } | |
| if uai := deploymentSa.Authentication.UserAssignedIdentityResourceId; uai != nil { | |
| ds.UserAssignedIdentityId = pointer.From(uai) | |
| } | |
| } |
|
|
||
| func FlattenBackendStorage(input string, backendStorageUseMsi bool) []BackendStorage { | ||
| backendStorageList := make([]BackendStorage, 0) | ||
| backendStorageString := input |
There was a problem hiding this comment.
why not directly name the first parameter as storageString
There was a problem hiding this comment.
updated, removed the variable backendStorageString and used input directly
|
|
||
| func FlattenStorageConnectionStrings(input webapps.StringDictionary) (backendStorageConnString string, backendStorageUseMsi bool, deploymentStorageConnString string) { | ||
| if input.Properties == nil { | ||
| return "", false, "" |
There was a problem hiding this comment.
simplify named return value with default value
| return "", false, "" | |
| return |
| deploymentStorageConnString = v | ||
| } | ||
| } | ||
| return backendStorageConnString, backendStorageUseMsi, deploymentStorageConnString |
There was a problem hiding this comment.
return with named return values
| return backendStorageConnString, backendStorageUseMsi, deploymentStorageConnString | |
| return |
|
Thanks @wuxu92 for review! All the comments are updated and acc tests passed: |
Community Note
Description
There are two types of storage account in flex consumption apps:
- Backend storage
- Deployment storage
In 4.0, there are 5 storage relate properties
storage_container_typestorage_container_endpointstorage_authentication_typestorage_access_keystorage_user_assigned_identity_idBoth backend and deployment storage is defined by them, they will be used to calculate the
AzureWebJobStoragewhich is used by backend storage andDEPLOYMENT_STORAGE_CONNECTION_STRINGwhich is used by deployment storage.In 4.0, if user doesn't specify the
backend_storage, theAzureWebJobStoragewill be calculated bystorage_access_keyandstorage_container_endpoint, otherwise, be calculated based inbackend_storageblock.In 4.0 user can only choose
deployment_storageor the old 5 storage related properties.In 5.0,
backend_storageanddeployment_storagewill be introduced and the old storage account properties will be deprecated.PR Checklist
For example: “
resource_name_here- description of change e.g. adding propertynew_property_name_here”Changes to existing Resource / Data Source
Testing
Change Log
Below please provide what should go into the changelog (if anything) conforming to the Changelog Format documented here.
azurerm_resource- support for thething1property [GH-00000]This is a (please select all that apply):
Related Issue(s)
Related to #28199 enhancement
Note
If this PR changes meaningfully during the course of review please update the title and description as required.