Add azurerm_playwright_workspace resource and data source#31954
Add azurerm_playwright_workspace resource and data source#31954v-yhyeo0202 wants to merge 16 commits intohashicorp:mainfrom
azurerm_playwright_workspace resource and data source#31954Conversation
There was a problem hiding this comment.
Pull request overview
Adds support for the Azure Load Test Service Playwright Workspace as a Terraform resource and data source.
Changes:
- Introduces
azurerm_playwright_workspaceresource + acceptance/identity tests. - Introduces
azurerm_playwright_workspacedata source + acceptance tests. - Wires up the new API client (PlaywrightWorkspaces) and updates docs/CI configuration & SDK dependency versions.
Reviewed changes
Copilot reviewed 12 out of 38 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| website/docs/r/playwright_workspace.html.markdown | Adds public docs for the new azurerm_playwright_workspace resource. |
| website/docs/d/playwright_workspace.html.markdown | Adds public docs for the new azurerm_playwright_workspace data source. |
| internal/services/loadtestservice/registration.go | Registers the new resource and data source in the Load Test service. |
| internal/services/loadtestservice/client/client.go | Adds and configures the Playwright Workspaces ARM client. |
| internal/services/loadtestservice/playwright_workspace_resource.go | Implements CRUD + identity for the new resource. |
| internal/services/loadtestservice/playwright_workspace_resource_test.go | Adds acceptance tests for create/import/update and requires-import behavior. |
| internal/services/loadtestservice/playwright_workspace_resource_identity_gen_test.go | Adds resource identity checks for the new resource. |
| internal/services/loadtestservice/playwright_workspace_data_source.go | Implements read logic and schema for the new data source. |
| internal/services/loadtestservice/playwright_workspace_data_source_test.go | Adds acceptance test coverage for the data source fields. |
| .teamcity/components/settings.kt | Adjusts test location overrides for the loadtestservice service. |
| .github/labeler-issue-triage.yml | Updates triage labeler to include the new resource/data source name. |
| go.mod / go.sum / vendor/modules.txt | Bumps go-azure-sdk versions and vendors the new playwrightworkspaces package. |
Comments suppressed due to low confidence (1)
website/docs/d/playwright_workspace.html.markdown:1
- This is a data source doc, so mentioning that changing
name'forces a new Playwright Workspace to be created' is incorrect (data sources don’t create resources). Recommend rewording to indicate it changes which existing workspace is looked up (e.g., 'The name of the existing Playwright Workspace to retrieve.').
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
| Required: true, | ||
| ForceNew: true, | ||
| ValidateFunc: validation.StringMatch( | ||
| regexp.MustCompile(`[a-zA-Z0-9-]{3,24}`), |
There was a problem hiding this comment.
The issue is fixed accordingly.
| } | ||
|
|
||
| func (PlaywrightWorkspaceDataSource) ModelObject() interface{} { | ||
| return nil |
There was a problem hiding this comment.
The issue is fixed accordingly.
| "name": { | ||
| Type: pluginsdk.TypeString, | ||
| Required: true, | ||
| ForceNew: true, |
There was a problem hiding this comment.
The issue is fixed accordingly.
| if _, err := client.Update(ctx, *id, param); err != nil { | ||
| return fmt.Errorf("updating %s: %+v", id, err) | ||
| } | ||
|
|
||
| return r.Read().Func(ctx, metadata) | ||
| }, |
There was a problem hiding this comment.
client.UpdateThenPoll is not available.
| Type: schema.TypeMap, | ||
| Optional: true, | ||
| ValidateFunc: tags.Validate, | ||
| Elem: &schema.Schema{ | ||
| Type: schema.TypeString, |
There was a problem hiding this comment.
The tags block is replaced with commonschema.Tags.
There was a problem hiding this comment.
Pull request overview
Adds support for Azure Load Test Service Playwright Workspaces by introducing a new azurerm_playwright_workspace resource and matching data source.
Changes:
- Implement
azurerm_playwright_workspaceresource + data source, including acceptance tests and identity tests. - Wire up the Load Test Service client/registration and update CI/test configuration to use supported locations.
- Add user-facing documentation and update dependency versions for the required SDK API package.
Reviewed changes
Copilot reviewed 13 out of 39 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
internal/services/loadtestservice/playwright_workspace_resource.go |
Implements the new Playwright Workspace Terraform resource CRUD. |
internal/services/loadtestservice/playwright_workspace_data_source.go |
Implements the new Playwright Workspace data source read logic. |
internal/services/loadtestservice/registration.go |
Registers the new resource and data source in the service package. |
internal/services/loadtestservice/client/client.go |
Adds a PlaywrightWorkspaces ARM client to the service autoclient. |
internal/services/loadtestservice/playwright_workspace_resource_test.go |
Adds acceptance coverage for create/import/update scenarios. |
internal/services/loadtestservice/playwright_workspace_resource_identity_gen_test.go |
Adds resource identity acceptance checks. |
internal/services/loadtestservice/playwright_workspace_data_source_test.go |
Adds acceptance coverage for the data source. |
website/docs/r/playwright_workspace.html.markdown |
Adds documentation for the new resource. |
website/docs/d/playwright_workspace.html.markdown |
Adds documentation for the new data source. |
.teamcity/components/settings.kt |
Overrides test locations for loadtestservice where Playwright is available. |
.github/labeler-issue-triage.yml |
Updates issue triage regex to include the new resource/data source. |
go.mod / go.sum / vendor/modules.txt |
Bumps go-azure-sdk and vendors the new Playwright Workspaces package. |
internal/services/resource/resource_group_resource_test.go |
Minor formatting change in an HCL test fixture string. |
Comments suppressed due to low confidence (1)
website/docs/d/playwright_workspace.html.markdown:1
- This is a data source, so “Changing this forces a new Playwright Workspace to be created” is misleading (it does not create resources). Please remove the “forces a new … to be created” sentence for the data source argument description.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
| @@ -0,0 +1,282 @@ | |||
| // Copyright IBM Corp. 2014, 2025 | |||
There was a problem hiding this comment.
Disagree due to wrong suggestion
| type AutoClient struct { | ||
| V20221201 loadtestserviceV20221201.Client | ||
| V20221201 loadtestserviceV20221201.Client | ||
| PlaywrightWorkspacesClient *playwrightworkspaces.PlaywrightWorkspacesClient | ||
| } |
There was a problem hiding this comment.
Disagree as the current format is widely applied
| return &AutoClient{ | ||
| V20221201: *v20221201Client, | ||
| V20221201: *v20221201Client, | ||
| PlaywrightWorkspacesClient: playwrightWorkspacesClient, |
There was a problem hiding this comment.
Disagree as the current format is widely applied
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
teowa
left a comment
There was a problem hiding this comment.
Hi @v-yhyeo0202, thanks for the PR, I've left some comments.
|
|
||
| resp, err := client.Get(ctx, *id) | ||
| if err != nil { | ||
| if response.WasNotFound(resp.HttpResponse) { | ||
| return metadata.MarkAsGone(id) | ||
| } | ||
|
|
||
| return fmt.Errorf("retrieving %s: %+v", id, err) | ||
| } |
There was a problem hiding this comment.
This part can be removed as the response model is never used
| resp, err := client.Get(ctx, *id) | |
| if err != nil { | |
| if response.WasNotFound(resp.HttpResponse) { | |
| return metadata.MarkAsGone(id) | |
| } | |
| return fmt.Errorf("retrieving %s: %+v", id, err) | |
| } |
There was a problem hiding this comment.
Thanks @teowa for the review. About this part, is it better to retain it for the checking of whether the resource is still there? In case the resource is absence, it will return to avoid calling the client.Update method (which will result in error if there is no resource to be updated). Although there is error catching at client.Update, is it ok to use that instead of metadata.MarkAsGone? I may not be experienced enough about Terraform plugin behavior, please explain to me if I miss anything.
There was a problem hiding this comment.
I agree with @v-yhyeo0202 , let's keep this if block because we still want the resource to be marked gone during update if the resource is deleted in portal.
There was a problem hiding this comment.
When we run terraform plan or terraform apply, Terraform refreshes the state first, triggering the Read() function. Since this process should already include MarkAsGone, it is not necessary to repeat this step during the Update(). Functionally, the check is OK here, but given that the resp model isn't used, it may be best to remove it. This is more of a style consideration from the contribution doc.
There was a problem hiding this comment.
Ok understand, I have removed the part accordingly.
| return err | ||
| } | ||
|
|
||
| return r.Read().Func(ctx, metadata) |
There was a problem hiding this comment.
This not the standard pattern. The SDK framework automatically calls Read() after Create() and Update()
| return r.Read().Func(ctx, metadata) | |
| return nil |
There was a problem hiding this comment.
The change is done accordingly.
| return fmt.Errorf("updating %s: %+v", id, err) | ||
| } | ||
|
|
||
| return r.Read().Func(ctx, metadata) |
There was a problem hiding this comment.
This not the standard pattern. The SDK framework automatically calls Read() after Create() and Update()
| return r.Read().Func(ctx, metadata) | |
| return nil |
There was a problem hiding this comment.
The change is done accordingly.
| if regionalAffinity := properties.RegionalAffinity; regionalAffinity != nil { | ||
| state.RegionalAffinityEnabled = pointer.From(regionalAffinity) == playwrightworkspaces.EnablementStatusEnabled | ||
| } |
There was a problem hiding this comment.
Since the default is true, should we set it as true if the property is missing from API response?
| if regionalAffinity := properties.RegionalAffinity; regionalAffinity != nil { | |
| state.RegionalAffinityEnabled = pointer.From(regionalAffinity) == playwrightworkspaces.EnablementStatusEnabled | |
| } | |
| state.RegionalAffinityEnabled = true | |
| if regionalAffinity := properties.RegionalAffinity; regionalAffinity != nil { | |
| state.RegionalAffinityEnabled = pointer.From(regionalAffinity) == playwrightworkspaces.EnablementStatusEnabled | |
| } |
There was a problem hiding this comment.
According to what I know, if a property is missing from API response, it is better to set state value to be same as Terraform configuration value to avoid perpetual difference? Please explain to me if I miss anything.
There was a problem hiding this comment.
Currently the regional_affinity_enabled property is removed to ensure similar behavior as that of portal.
There was a problem hiding this comment.
I think I understand what you mean now. The property can sometimes be missing from the API response when the user does not specify properties and properties are not returned. In this case, the default value should be set to avoid perpetual difference, am I right?
There was a problem hiding this comment.
I think I understand what you mean now. The property can sometimes be missing from the API response when the user does not specify
propertiesandpropertiesare not returned. In this case, the default value should be set to avoid perpetual difference, am I right?
Yes, I mean so.
|
|
||
| The following arguments are supported: | ||
|
|
||
| * `name` - (Required) The name of this Playwright Workspace. Changing this forces a new Playwright Workspace to be created. |
There was a problem hiding this comment.
| * `name` - (Required) The name of this Playwright Workspace. Changing this forces a new Playwright Workspace to be created. | |
| * `name` - (Required) The name of this Playwright Workspace. |
There was a problem hiding this comment.
The change is done accordingly.
gerrytan
left a comment
There was a problem hiding this comment.
Thanks @v-yhyeo0202 , there are some missing feature compared to portal that I cannot locate the API for:
- Reporting storage
- Access management
- Region management
I'm not sure if this service really ready to onboard to AzureRM.
| "loadbalancer" to testConfiguration(locationOverride = LocationConfiguration("westeurope", "eastus2", "westus", false)), | ||
|
|
||
| // Playwright workspace is only available in certain locations | ||
| "loadtestservice" to testConfiguration(locationOverride = LocationConfiguration("westeurope", "eastus", "westus2", false)), |
There was a problem hiding this comment.
Thanks @gerrytan for the review. The change is done accordingly.
|
|
||
| "location": commonschema.Location(), | ||
|
|
||
| "local_auth_enabled": { |
There was a problem hiding this comment.
It's weird how I don't see local_auth_enabled and regional_affinity_enabled in portal.
I recommend only surfacing fields currently available in portal for now to align the experiences.
If I create the resource via portal, I get the following defaults:
"regionalAffinity": "Enabled",
"localAuth": "Disabled",
Let's just supply these defaults in the Create method for now.
Also the reporting toggle (which allow storage account linking) is missing
There was a problem hiding this comment.
Ok understand. I will remove local_auth_enabled and regional_affinity_enabled properties from schema. However, about reporting storage, it seems to be one of the features listed in the issue. If it is not ready, does it mean that the issue cannot be solved yet?
There was a problem hiding this comment.
about reporting storage, it seems to be one of the features listed in the issue. If it is not ready, does it mean that the issue cannot be solved yet?
After I look at the feature again, I'm ok with this TF resource to be released to users without "reporting storage" and other missing features. Hopefully the API for those will be released soon so this can have parity.
gerrytan
left a comment
There was a problem hiding this comment.
Thank you @v-yhyeo0202 , I left some small concern but this PR is looking good overall.
|
|
||
| * `id` - The ID of the Playwright Workspace. | ||
|
|
||
| * `dataplane_uri` - The data plane service API URI of the Playwright Workspace. |
There was a problem hiding this comment.
Can we add an example value of this URI as per the doc: https://learn.microsoft.com/en-us/azure/app-testing/playwright-workspaces/quickstart-run-end-to-end-tests?tabs=playwrightcli&pivots=playwright-test-runner
So it's obvious this is the attribute TF user should read from for PLAYWRIGHT_SERVICE_URL env var.
There was a problem hiding this comment.
Thanks @gerrytan for the review. The URI format is added with the link to the documentation.
| param.Tags = pointer.To(config.Tags) | ||
| } | ||
|
|
||
| if _, err := client.Update(ctx, *id, param); err != nil { |
There was a problem hiding this comment.
Please use CreateOrUpdateThenPoll in here to benefit from the LRO polling available in the SDK.
There was a problem hiding this comment.
I have tried CreateOrUpdateThenPoll previously but the method fails to update the tags property. The REST API requests and responses shown below are tested again on 24 March and seems that the issue still persists:
Create request
az rest --method PUT \
--url https://management.azure.com/subscriptions/REDACTED/resourceGroups/REDACTED/providers/Microsoft.LoadTestService/playwrightWorkspaces/REDACTED?api-version=2025-09-01 \
--headers "Content-Type=application/json" \
--body '
{
"location": "eastasia",
"properties": {
"localAuth": "Disabled",
"regionalAffinity": "Enabled"
},
"tags": null
}'Create response
{
"id": "/subscriptions/REDACTED/resourceGroups/REDACTED/providers/Microsoft.LoadTestService/playwrightWorkspaces/REDACTED",
"location": "eastasia",
"name": "REDACTED",
"properties": {
"dataplaneUri": "https://eastasia.api.playwright.microsoft.com/playwrightworkspaces/REDACTED",
"localAuth": "Disabled",
"provisioningState": "Accepted",
"regionalAffinity": "Enabled",
"workspaceId": "REDACTED"
},
"systemData": {
"createdAt": "REDACTED",
"createdBy": "REDACTED",
"createdByType": "User",
"lastModifiedAt": "REDACTED",
"lastModifiedBy": "REDACTED",
"lastModifiedByType": "User"
},
"type": "microsoft.loadtestservice/playwrightworkspaces"
}Update request
az rest --method PUT \
--url https://management.azure.com/subscriptions/REDACTED/resourceGroups/REDACTED/providers/Microsoft.LoadTestService/playwrightWorkspaces/REDACTED?api-version=2025-09-01 \
--headers "Content-Type=application/json" \
--body '
{
"id": "/subscriptions/REDACTED/resourceGroups/REDACTED/providers/Microsoft.LoadTestService/playwrightWorkspaces/REDACTED",
"location": "eastasia",
"name": "REDACTED",
"properties": {
"dataplaneUri": "https://eastasia.api.playwright.microsoft.com/playwrightworkspaces/REDACTED",
"localAuth": "Disabled",
"provisioningState": "Succeeded",
"regionalAffinity": "Enabled",
"workspaceId": "REDACTED"
},
"systemData": null,
"tags": {
"Environment": "Sandbox",
"Label": "Test"
},
"type": "microsoft.loadtestservice/playwrightworkspaces"
}'Update response
{
"id": "/subscriptions/REDACTED/resourceGroups/REDACTED/providers/Microsoft.LoadTestService/playwrightWorkspaces/REDACTED",
"location": "eastasia",
"name": "REDACTED",
"properties": {
"dataplaneUri": "https://eastasia.api.playwright.microsoft.com/playwrightworkspaces/REDACTED",
"localAuth": "Disabled",
"provisioningState": "Accepted",
"regionalAffinity": "Enabled",
"workspaceId": "REDACTED"
},
"systemData": {
"createdAt": "REDACTED",
"createdBy": "REDACTED",
"createdByType": "User",
"lastModifiedAt": "REDACTED",
"lastModifiedBy": "REDACTED",
"lastModifiedByType": "User"
},
"type": "microsoft.loadtestservice/playwrightworkspaces"
}The tags property is not updated after the update request is issued. I have contacted the service team member who writes the OpenAPI specifications previously about this issue. She mentions that this should be handled by ARM instead of RPaaS RP and suggests me to use PATCH (Update) method instead as shown in the Teams chat screenshot below. Should I try to find the service team member who handle ARM of this resource?
|
|
||
| tags = { | ||
| env = "prod" | ||
| } |
There was a problem hiding this comment.
Minor: by convention we only put require properties for basic tests. tags is not necessary here. Instead you should have another test complete which include tags.
There was a problem hiding this comment.
Change is done accordingly.
| github.com/hashicorp/go-azure-sdk/resource-manager v0.20260306.1182644 | ||
| github.com/hashicorp/go-azure-sdk/sdk v0.20260306.1182644 | ||
| github.com/hashicorp/go-azure-sdk/resource-manager v0.20260312.1165223 | ||
| github.com/hashicorp/go-azure-sdk/sdk v0.20260312.1165223 |
There was a problem hiding this comment.
Please hold off merging this, typically HC will coordinate the go-azure-sdk bump separately because they need to run a wider coverage acctest suite first. If we do this bump in this PR we might accidentally break unrelated part of AzureRM.
There was a problem hiding this comment.
Seems that the version in main branch is updated currently.
|
|
||
| "location": commonschema.Location(), | ||
|
|
||
| "local_auth_enabled": { |
There was a problem hiding this comment.
about reporting storage, it seems to be one of the features listed in the issue. If it is not ready, does it mean that the issue cannot be solved yet?
After I look at the feature again, I'm ok with this TF resource to be released to users without "reporting storage" and other missing features. Hopefully the API for those will be released soon so this can have parity.



Community Note
Description
azurerm_playwright_workspaceresource and data source is added in this PR.OpenAPI specification: https://github.com/Azure/azure-rest-api-specs/blob/main/specification/loadtestservice/resource-manager/Microsoft.LoadTestService/playwright/stable/2025-09-01/playwright.json
Documentation: https://learn.microsoft.com/en-us/rest/api/playwright/resourcemanager/playwright-workspaces?view=rest-playwright-resourcemanager-2025-09-01
The files to be reviewed are listed below.
PR Checklist
For example: “
resource_name_here- description of change e.g. adding propertynew_property_name_here”Changes to existing Resource / Data Source
(For changes that include a state migration only). I have manually tested the migration path between relevant versions of the provider.Testing
The tests are run with parallelism equal to 1 due to rate limiting issue of Playwright Workspace.

https://hashicorp.teamcity.com/buildConfiguration/TF_AzureRM_AZURERM_SERVICE_PUBLIC_LOADTESTSERVICE/635163?buildTab=overview
Change Log
Below please provide what should go into the changelog (if anything) conforming to the Changelog Format documented here.
azurerm_playwright_workspace- add resource and data sourceThis is a (please select all that apply):
Related Issue(s)
Fixes #31655
Rollback Plan
If a change needs to be reverted, we will publish an updated version of the provider.
Note
If this PR changes meaningfully during the course of review please update the title and description as required.