Repository: pulumi/pulumi-terraform-bridge
Pull request #1550 (2023-12-01 00:25:35)

Allow the `Type` override to effect downstream types
This PR changes how `tfbridge.SchemaInfo.Type` is interpreted when applied to object properties.

Previously, type changed the `schema.TypeSpec.{Type,Ref}` fields on the generated resource. It did not effect any nested types.

With this PR, `Type` is interpreted as a command to rename the relevant object and its entire nested structure. This allows multiple fields or objects to share a type definition when specified, and will allow us to reduce the size of our SDKs.

---

This is technically a breaking change. Bridge users who have specified `Type: someNewType` on object types will see different behavior. In practice, setting `Type` on an object would generate an invalid schema without a careful fix-up. I suspect that no-one does this.

I have tested this change on `pulumi-ns1`, `pulumi-aws`, `pulumi-gcp` and `pulumi-azure`. None of these providers are effected by this change.

### Implementers Notes

`paths.TypePath` is used to determine the module of derived types. Since we want subtypes of a moved type to themselves move to the module of their parent, we need to track that in the paths used. To allow inserting a type at an arbitrary module, it was necessary to create a new `paths.TypePath`; `paths.RawTypePath` projects a `tokens.Type` into `paths.TypePath` so it can be used for module discovery.

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/pulumi/pulumi-terraform-bridge/pull/1550?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pulumi) Report
Attention: `6 lines` in your changes are missing coverage. Please review.
> Comparison is base [(`6385710`)](https://app.codecov.io/gh/pulumi/pulumi-terraform-bridge/commit/6385710fcbc4c506107433c805ec0afe8e536833?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pulumi) 58.19% compared to head [(`19dade1`)](https://app.codecov.io/gh/pulumi/pulumi-terraform-bridge/pull/1550?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pulumi) 58.18%.

| [Files](https://app.codecov.io/gh/pulumi/pulumi-terraform-bridge/pull/1550?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pulumi) | Patch % | Lines |
|---|---|---|
| [pkg/tfgen/generate\_schema.go](https://app.codecov.io/gh/pulumi/pulumi-terraform-bridge/pull/1550?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pulumi#diff-cGtnL3RmZ2VuL2dlbmVyYXRlX3NjaGVtYS5nbw==) | 25.00% | [4 Missing and 2 partials :warning: ](https://app.codecov.io/gh/pulumi/pulumi-terraform-bridge/pull/1550?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pulumi) |

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##           master    #1550      +/-   ##
==========================================
- Coverage   58.19%   58.18%   -0.01%     
==========================================
  Files         288      287       -1     
  Lines       40040    40044       +4     
==========================================
  Hits        23301    23301              
- Misses      15403    15405       +2     
- Partials     1336     1338       +2     
```



</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/pulumi/pulumi-terraform-bridge/pull/1550?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pulumi).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pulumi).

t0yv0: I like the spirit of this change very much. To paraphrase what I am seeing, it looks like we allow
the user to trigger deliberate collisions when allocating Pulumi type tokens to nested object paths.
These collisions be used to improved sharing, instead of allocating 2 pulumi tokens (and TypeScript
classes and so forth), the user can now have one.

The chief question I have is this: does this work for pseudo-recursion scenario such as wafv2 types? TLDR is that TF model does not support observable sharing recursion so they encode recursive types with "up to depth N" with say N=5, this causes type explosion and bloat at many levels. This PR relies on pre-written `.equals()` function to decide structural equality of "deliberately-colliding types". For the Wafv2 scenario will it work? Naively I'd think that genWafType(4) != genWafType(5) deliberately and this scenario will hit the case where the bridge things the user is making a mistake assigning an identical token to these supplementary types.

I also have a few nits on the implementation level that make it harder for me to follow the change, which I'd love to talk through, as perhaps they point to a gap in my understanding how this codebase works. Let me add some comments.

Patch:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index fad0db01..efd47c26 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -41,7 +41,7 @@ jobs:
             skip-cache: true
             skip-pkg-cache: true
             skip-build-cache: true
-            version: v1.53
+            version: v1.55
       - name: Lint
         run: make lint
       - name: Test
diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml
index 8ab10e67..b01163bb 100644
--- a/.github/workflows/master.yml
+++ b/.github/workflows/master.yml
@@ -44,7 +44,7 @@ jobs:
             skip-cache: true
             skip-pkg-cache: true
             skip-build-cache: true
-            version: v1.51.2
+            version: v1.55
       - name: Lint
         run: make lint
       - name: Test
diff --git a/.github/workflows/weekly-pulumi-update.yml b/.github/workflows/weekly-pulumi-update.yml
index a0a71a8d..0f020464 100644
--- a/.github/workflows/weekly-pulumi-update.yml
+++ b/.github/workflows/weekly-pulumi-update.yml
@@ -32,7 +32,7 @@ jobs:
           skip-cache: true
           skip-pkg-cache: true
           skip-build-cache: true
-          version: v1.51.2
+          version: v1.55
     - name: Update Pulumi/Pulumi
       id: gomod
       run: >-
diff --git a/go.mod b/go.mod
index 48c2c2d0..2260da96 100644
--- a/go.mod
+++ b/go.mod
@@ -41,7 +41,7 @@ require (
 	github.com/pulumi/pulumi-java/pkg v0.9.8
 	github.com/pulumi/pulumi-terraform-bridge/testing v0.0.1
 	github.com/pulumi/pulumi-terraform-bridge/x/muxer v0.0.4
-	github.com/pulumi/pulumi-yaml v1.4.2
+	github.com/pulumi/pulumi-yaml v1.4.3
 	github.com/pulumi/schema-tools v0.1.2
 	github.com/pulumi/terraform-diff-reader v0.0.2
 	github.com/russross/blackfriday/v2 v2.1.0
@@ -49,10 +49,10 @@ require (
 	github.com/spf13/cobra v1.7.0
 	github.com/stretchr/testify v1.8.4
 	github.com/zclconf/go-cty v1.14.0
-	golang.org/x/crypto v0.14.0
-	golang.org/x/mod v0.13.0
-	golang.org/x/net v0.17.0
-	golang.org/x/text v0.13.0
+	golang.org/x/crypto v0.15.0
+	golang.org/x/mod v0.14.0
+	golang.org/x/net v0.18.0
+	golang.org/x/text v0.14.0
 	google.golang.org/grpc v1.57.1
 	google.golang.org/protobuf v1.31.0
 	gotest.tools/v3 v3.0.3
@@ -72,18 +72,21 @@ require (
 	github.com/charmbracelet/lipgloss v0.7.1 // indirect
 	github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect
 	github.com/cyphar/filepath-securejoin v0.2.4 // indirect
+	github.com/deckarep/golang-set/v2 v2.5.0 // indirect
 	github.com/google/s2a-go v0.1.4 // indirect
 	github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
 	github.com/mattn/go-localereader v0.0.1 // indirect
-	github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b // indirect
+	github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
 	github.com/muesli/reflow v0.3.0 // indirect
-	github.com/muesli/termenv v0.15.1 // indirect
-	github.com/pulumi/esc v0.6.0 // indirect
+	github.com/muesli/termenv v0.15.2 // indirect
+	github.com/pgavlin/fx v0.1.6 // indirect
+	github.com/pulumi/appdash v0.0.0-20231130102222-75f619a67231 // indirect
+	github.com/pulumi/esc v0.6.1-0.20231111193429-44b746a5b3b5 // indirect
 	github.com/shopspring/decimal v1.3.1 // indirect
 	github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect
 	github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
-	golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect
-	golang.org/x/tools v0.14.0 // indirect
+	golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa // indirect
+	golang.org/x/tools v0.15.0 // indirect
 	google.golang.org/genproto/googleapis/api v0.0.0-20230706204954-ccb25ca9f130 // indirect
 	google.golang.org/genproto/googleapis/rpc v0.0.0-20230706204954-ccb25ca9f130 // indirect
 )
@@ -170,7 +173,7 @@ require (
 	github.com/hashicorp/golang-lru v0.5.4 // indirect
 	github.com/hashicorp/logutils v1.0.0 // indirect
 	github.com/hashicorp/terraform-plugin-go v0.19.0 // indirect
-	github.com/hashicorp/terraform-plugin-log v0.9.0
+	github.com/hashicorp/terraform-plugin-log v0.9.0 // indirect
 	github.com/hashicorp/terraform-registry-address v0.2.2 // indirect
 	github.com/hashicorp/vault/api v1.8.2 // indirect
 	github.com/hashicorp/vault/sdk v0.6.1 // indirect
@@ -184,8 +187,8 @@ require (
 	github.com/kevinburke/ssh_config v1.2.0 // indirect
 	github.com/klauspost/compress v1.15.11 // indirect
 	github.com/mattn/go-colorable v0.1.12 // indirect
-	github.com/mattn/go-isatty v0.0.18 // indirect
-	github.com/mattn/go-runewidth v0.0.14 // indirect
+	github.com/mattn/go-isatty v0.0.19 // indirect
+	github.com/mattn/go-runewidth v0.0.15 // indirect
 	github.com/mitchellh/go-ps v1.0.0 // indirect
 	github.com/mitchellh/go-wordwrap v1.0.1 // indirect
 	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
@@ -194,15 +197,15 @@ require (
 	github.com/natefinch/atomic v1.0.1 // indirect
 	github.com/oklog/run v1.1.0 // indirect
 	github.com/opentracing/basictracer-go v1.1.0 // indirect
-	github.com/opentracing/opentracing-go v1.2.0 // indirect
+	github.com/opentracing/opentracing-go v1.2.0
 	github.com/pgavlin/goldmark v1.1.33-0.20200616210433-b5eb04559386 // indirect
 	github.com/pierrec/lz4 v2.6.1+incompatible // indirect
 	github.com/pjbgf/sha1cd v0.3.0 // indirect
 	github.com/pkg/term v1.1.0 // indirect
 	github.com/pmezard/go-difflib v1.0.0 // indirect
 	github.com/posener/complete v1.2.3 // indirect
-	github.com/pulumi/pulumi/pkg/v3 v3.94.2
-	github.com/pulumi/pulumi/sdk/v3 v3.94.2
+	github.com/pulumi/pulumi/pkg/v3 v3.96.2
+	github.com/pulumi/pulumi/sdk/v3 v3.96.2
 	github.com/rivo/uniseg v0.4.4 // indirect
 	github.com/rogpeppe/go-internal v1.11.0 // indirect
 	github.com/ryanuber/go-glob v1.0.0 // indirect
@@ -230,9 +233,9 @@ require (
 	gocloud.dev v0.27.0 // indirect
 	gocloud.dev/secrets/hashivault v0.27.0 // indirect
 	golang.org/x/oauth2 v0.8.0 // indirect
-	golang.org/x/sync v0.4.0 // indirect
-	golang.org/x/sys v0.13.0 // indirect
-	golang.org/x/term v0.13.0 // indirect
+	golang.org/x/sync v0.5.0 // indirect
+	golang.org/x/sys v0.14.0 // indirect
+	golang.org/x/term v0.14.0 // indirect
 	golang.org/x/time v0.3.0 // indirect
 	golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
 	google.golang.org/api v0.126.0 // indirect
@@ -242,7 +245,6 @@ require (
 	gopkg.in/warnings.v0 v0.1.2 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
 	lukechampine.com/frand v1.4.2 // indirect
-	sourcegraph.com/sourcegraph/appdash v0.0.0-20211028080628-e2786a622600 // indirect
 )
 
 replace github.com/hashicorp/terraform-plugin-sdk/v2 => github.com/pulumi/terraform-plugin-sdk/v2 v2.0.0-20230912190043-e6d96b3b8f7e
diff --git a/go.sum b/go.sum
index 8ad776b9..fbd807c7 100644
--- a/go.sum
+++ b/go.sum
@@ -1095,6 +1095,8 @@ github.com/d2g/hardwareaddr v0.0.0-20190221164911-e7d9fbe030e4/go.mod h1:bMl4RjI
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/deckarep/golang-set/v2 v2.5.0 h1:hn6cEZtQ0h3J8kFrHR/NrzyOoTnjgW1+FmNJzQ7y/sA=
+github.com/deckarep/golang-set/v2 v2.5.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4=
 github.com/denisenkom/go-mssqldb v0.12.2/go.mod h1:lnIw1mZukFRZDJYQ0Pb833QS2IaC3l5HkEfra2LJ+sk=
 github.com/dennwc/varint v1.0.0/go.mod h1:hnItb35rvZvJrbTALZtY/iQfDs48JKRG1RPpgziApxA=
 github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0=
@@ -1854,15 +1856,15 @@ github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOA
 github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
 github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
 github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
-github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98=
-github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
+github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
+github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
 github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
 github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
 github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
 github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
 github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
-github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
-github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
+github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
+github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
 github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
 github.com/mattn/go-shellwords v1.0.6/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
 github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
@@ -1942,14 +1944,14 @@ github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJ
 github.com/montanaflynn/stats v0.6.6/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
 github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
 github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ=
-github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b h1:1XF24mVaiu7u+CFywTdcDo2ie1pzzhwjt6RHqzpMU34=
-github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b/go.mod h1:fQuZ0gauxyBcmsdE3ZT4NasjaRdxmbCS0jRHsrWu3Ho=
+github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI=
+github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo=
 github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
 github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
 github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
 github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
-github.com/muesli/termenv v0.15.1 h1:UzuTb/+hhlBugQz28rpzey4ZuKcZ03MeKsoG7IJZIxs=
-github.com/muesli/termenv v0.15.1/go.mod h1:HeAQPTzpfs016yGtA4g00CsdYnVLJvxsS4ANqrZs2sQ=
+github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo=
+github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8=
 github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
 github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
 github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
@@ -2050,6 +2052,8 @@ github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrap
 github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
 github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac=
 github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
+github.com/pgavlin/fx v0.1.6 h1:r9jEg69DhNoCd3Xh0+5mIbdbS3PqWrVWujkY76MFRTU=
+github.com/pgavlin/fx v0.1.6/go.mod h1:KWZJ6fqBBSh8GxHYqwYCf3rYE7Gp2p0N8tJp8xv9u9M=
 github.com/pgavlin/goldmark v1.1.33-0.20200616210433-b5eb04559386 h1:LoCV5cscNVWyK5ChN/uCoIFJz8jZD63VQiGJIRgr6uo=
 github.com/pgavlin/goldmark v1.1.33-0.20200616210433-b5eb04559386/go.mod h1:MRxHTJrf9FhdfNQ8Hdeh9gmHevC9RJE/fu8M3JIGjoE=
 github.com/phpdave11/gofpdf v1.4.2/go.mod h1:zpO6xFn9yxo3YLyMvW8HcKWVdbNqgIfOOp2dXMnm1mY=
@@ -2140,16 +2144,18 @@ github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1
 github.com/prometheus/prometheus v0.35.0/go.mod h1:7HaLx5kEPKJ0GDgbODG0fZgXbQ8K/XjZNJXQmbmgQlY=
 github.com/prometheus/prometheus v0.37.0/go.mod h1:egARUgz+K93zwqsVIAneFlLZefyGOON44WyAp4Xqbbk=
 github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
-github.com/pulumi/esc v0.6.0 h1:m8jWgBektlj26RvrN3/sM0r1lYiwTMmqVPvLxCxahGE=
-github.com/pulumi/esc v0.6.0/go.mod h1:Y6W21yUukvxS2NnS5ae1beMSPhMvj0xNAYcDqDHVj/g=
+github.com/pulumi/appdash v0.0.0-20231130102222-75f619a67231 h1:vkHw5I/plNdTr435cARxCW6q9gc0S/Yxz7Mkd38pOb0=
+github.com/pulumi/appdash v0.0.0-20231130102222-75f619a67231/go.mod h1:murToZ2N9hNJzewjHBgfFdXhZKjY3z5cYC1VXk+lbFE=
+github.com/pulumi/esc v0.6.1-0.20231111193429-44b746a5b3b5 h1:1DJMji9F7XPea46bSuhy4/85n8J4/Mfz8PWLZtjKKiI=
+github.com/pulumi/esc v0.6.1-0.20231111193429-44b746a5b3b5/go.mod h1:Y6W21yUukvxS2NnS5ae1beMSPhMvj0xNAYcDqDHVj/g=
 github.com/pulumi/pulumi-java/pkg v0.9.8 h1:c8mYsalnRXA2Ibgvv6scefOn6mW1Vb0UT0mcDqjsivQ=
 github.com/pulumi/pulumi-java/pkg v0.9.8/go.mod h1:c6rSw/+q4O0IImgJ9axxoC6QesbPYWBaG5gimbHouUQ=
-github.com/pulumi/pulumi-yaml v1.4.2 h1:LKptNH2lG6f5IjJmR7bvu7ouBE4sgkTRNd4I8ZHmjHw=
-github.com/pulumi/pulumi-yaml v1.4.2/go.mod h1:1hwsXFnT7hm2ZeftY5fzjkngjs2eT/rv9MsPE48Leb4=
-github.com/pulumi/pulumi/pkg/v3 v3.94.2 h1:b+gQ+EJZMYfSIu/sb/+p36VmfZ2ZJWbEWMsptt2f6Kk=
-github.com/pulumi/pulumi/pkg/v3 v3.94.2/go.mod h1:aCeDMlncyDwX3RG6+vYUjWsy6bOEtoHUY0twCcn5qqE=
-github.com/pulumi/pulumi/sdk/v3 v3.94.2 h1:B8W3CCzA2fX/0730o/7KQXdmqrY5KdgGtiAA6rDMVlk=
-github.com/pulumi/pulumi/sdk/v3 v3.94.2/go.mod h1:zeqyIODqbb6GrEyhFV6aJET/xBSXSnF7Bw/EjbYZUnU=
+github.com/pulumi/pulumi-yaml v1.4.3 h1:GO36c7FTl8If20Dn/w2Hi3huP7kmsO00KNaz3GJU0Ws=
+github.com/pulumi/pulumi-yaml v1.4.3/go.mod h1:1hwsXFnT7hm2ZeftY5fzjkngjs2eT/rv9MsPE48Leb4=
+github.com/pulumi/pulumi/pkg/v3 v3.96.2 h1:d+zDsh4K1j90q/upDRkhgxHkspP20Xxd3g6OaMbw574=
+github.com/pulumi/pulumi/pkg/v3 v3.96.2/go.mod h1:lR+q06XrnEqRzJXZ6IeHb4nu+U5fswFcc51Q0MRBTio=
+github.com/pulumi/pulumi/sdk/v3 v3.96.2 h1:q5ZKdf+e9JR+a6Eiueg0Ohy6jCQGk9pO2V7xI/qGP3I=
+github.com/pulumi/pulumi/sdk/v3 v3.96.2/go.mod h1:yvD23IIRiqIXuo4kaZNe5zK/uT0nhO99wr6BVEqoi7A=
 github.com/pulumi/schema-tools v0.1.2 h1:Fd9xvUjgck4NA+7/jSk7InqCUT4Kj940+EcnbQKpfZo=
 github.com/pulumi/schema-tools v0.1.2/go.mod h1:62lgj52Tzq11eqWTIaKd+EVyYAu5dEcDJxMhTjvMO/k=
 github.com/pulumi/terraform-diff-reader v0.0.2 h1:kTE4nEXU3/SYXESvAIem+wyHMI3abqkI3OhJ0G04LLI=
@@ -2530,8 +2536,8 @@ golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU
 golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
 golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
 golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
-golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
-golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
+golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA=
+golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g=
 golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@@ -2547,8 +2553,8 @@ golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u0
 golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
 golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
 golang.org/x/exp v0.0.0-20220827204233-334a2380cb91/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
-golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
-golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
+golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ=
+golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE=
 golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
 golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
 golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
@@ -2594,8 +2600,8 @@ golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
 golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
 golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
 golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
-golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY=
-golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
+golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
+golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
 golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -2692,8 +2698,8 @@ golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
 golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
 golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
 golang.org/x/net v0.13.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
-golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
-golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
+golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg=
+golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -2747,8 +2753,8 @@ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJ
 golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ=
-golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
+golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
+golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
 golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -2909,8 +2915,8 @@ golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
-golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q=
+golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
@@ -2927,8 +2933,8 @@ golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
 golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
 golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o=
 golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
-golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek=
-golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
+golang.org/x/term v0.14.0 h1:LGK9IlZ8T9jvdy6cTdfKUCltatMFOehAQo9SRC46UQ8=
+golang.org/x/term v0.14.0/go.mod h1:TySc+nGkYR6qt8km8wUhuFRTVSMIX3XPR58y2lC8vww=
 golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -2946,8 +2952,9 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
 golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
 golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
 golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
-golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
 golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
+golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
+golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
 golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -3056,8 +3063,8 @@ golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA=
 golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k=
 golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
 golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s=
-golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc=
-golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg=
+golang.org/x/tools v0.15.0 h1:zdAyfUGbYmuVokhzVmghFl2ZJh5QhcfebBgmVPFYA+8=
+golang.org/x/tools v0.15.0/go.mod h1:hpksKq4dtpQWS1uQ61JkdqWM3LscIS6Slf+VVkm+wQk=
 golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -3565,5 +3572,3 @@ sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
 sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
 sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
 sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU=
-sourcegraph.com/sourcegraph/appdash v0.0.0-20211028080628-e2786a622600 h1:hfyJ5ku9yFtLVOiSxa3IN+dx5eBQT9mPmKFypAmg8XM=
-sourcegraph.com/sourcegraph/appdash v0.0.0-20211028080628-e2786a622600/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU=
diff --git a/pf/go.mod b/pf/go.mod
index 060431a6..69949505 100644
--- a/pf/go.mod
+++ b/pf/go.mod
@@ -10,12 +10,13 @@ replace (
 require (
 	github.com/blang/semver v3.5.1+incompatible
 	github.com/golang/protobuf v1.5.3
+	github.com/hashicorp/go-hclog v1.5.0
 	github.com/hashicorp/go-multierror v1.1.1
 	github.com/hashicorp/terraform-plugin-framework v1.4.1
 	github.com/hashicorp/terraform-plugin-framework-validators v0.10.0
 	github.com/hashicorp/terraform-plugin-go v0.19.0
 	github.com/hashicorp/terraform-plugin-log v0.9.0
-	github.com/pulumi/pulumi-terraform-bridge/v3 v3.66.0
+	github.com/pulumi/pulumi-terraform-bridge/v3 v3.68.0
 	github.com/pulumi/pulumi-terraform-bridge/x/muxer v0.0.6
 	github.com/stretchr/testify v1.8.4
 	google.golang.org/grpc v1.57.1
@@ -35,20 +36,22 @@ require (
 	github.com/charmbracelet/lipgloss v0.7.1 // indirect
 	github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect
 	github.com/cyphar/filepath-securejoin v0.2.4 // indirect
+	github.com/deckarep/golang-set/v2 v2.5.0 // indirect
 	github.com/gedex/inflector v0.0.0-20170307190818-16278e9db813 // indirect
 	github.com/google/s2a-go v0.1.4 // indirect
-	github.com/hashicorp/go-hclog v1.5.0 // indirect
 	github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
 	github.com/mattn/go-localereader v0.0.1 // indirect
-	github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b // indirect
+	github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
 	github.com/muesli/reflow v0.3.0 // indirect
-	github.com/muesli/termenv v0.15.1 // indirect
-	github.com/pulumi/esc v0.6.0 // indirect
+	github.com/muesli/termenv v0.15.2 // indirect
+	github.com/pgavlin/fx v0.1.6 // indirect
+	github.com/pulumi/appdash v0.0.0-20231130102222-75f619a67231 // indirect
+	github.com/pulumi/esc v0.6.1-0.20231111193429-44b746a5b3b5 // indirect
 	github.com/shopspring/decimal v1.3.1 // indirect
 	github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect
 	github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
-	golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect
-	golang.org/x/tools v0.14.0 // indirect
+	golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa // indirect
+	golang.org/x/tools v0.15.0 // indirect
 	google.golang.org/genproto/googleapis/api v0.0.0-20230706204954-ccb25ca9f130 // indirect
 	google.golang.org/genproto/googleapis/rpc v0.0.0-20230706204954-ccb25ca9f130 // indirect
 )
@@ -159,8 +162,8 @@ require (
 	github.com/kevinburke/ssh_config v1.2.0 // indirect
 	github.com/klauspost/compress v1.15.11 // indirect
 	github.com/mattn/go-colorable v0.1.12 // indirect
-	github.com/mattn/go-isatty v0.0.18 // indirect
-	github.com/mattn/go-runewidth v0.0.14 // indirect
+	github.com/mattn/go-isatty v0.0.19 // indirect
+	github.com/mattn/go-runewidth v0.0.15 // indirect
 	github.com/mitchellh/cli v1.1.5 // indirect
 	github.com/mitchellh/copystructure v1.2.0 // indirect
 	github.com/mitchellh/go-homedir v1.1.0 // indirect
@@ -176,7 +179,7 @@ require (
 	github.com/natefinch/atomic v1.0.1 // indirect
 	github.com/oklog/run v1.1.0 // indirect
 	github.com/opentracing/basictracer-go v1.1.0 // indirect
-	github.com/opentracing/opentracing-go v1.2.0 // indirect
+	github.com/opentracing/opentracing-go v1.2.0
 	github.com/pgavlin/goldmark v1.1.33-0.20200616210433-b5eb04559386 // indirect
 	github.com/pierrec/lz4 v2.6.1+incompatible // indirect
 	github.com/pjbgf/sha1cd v0.3.0 // indirect
@@ -185,9 +188,9 @@ require (
 	github.com/pmezard/go-difflib v1.0.0 // indirect
 	github.com/posener/complete v1.2.3 // indirect
 	github.com/pulumi/pulumi-java/pkg v0.9.8 // indirect
-	github.com/pulumi/pulumi-yaml v1.4.2 // indirect
-	github.com/pulumi/pulumi/pkg/v3 v3.94.2
-	github.com/pulumi/pulumi/sdk/v3 v3.94.2
+	github.com/pulumi/pulumi-yaml v1.4.3 // indirect
+	github.com/pulumi/pulumi/pkg/v3 v3.96.2
+	github.com/pulumi/pulumi/sdk/v3 v3.96.2
 	github.com/pulumi/schema-tools v0.1.2 // indirect
 	github.com/rivo/uniseg v0.4.4 // indirect
 	github.com/rogpeppe/go-internal v1.11.0 // indirect
@@ -217,14 +220,14 @@ require (
 	go.uber.org/atomic v1.9.0 // indirect
 	gocloud.dev v0.27.0 // indirect
 	gocloud.dev/secrets/hashivault v0.27.0 // indirect
-	golang.org/x/crypto v0.14.0 // indirect
-	golang.org/x/mod v0.13.0 // indirect
-	golang.org/x/net v0.17.0 // indirect
+	golang.org/x/crypto v0.15.0 // indirect
+	golang.org/x/mod v0.14.0 // indirect
+	golang.org/x/net v0.18.0 // indirect
 	golang.org/x/oauth2 v0.8.0 // indirect
-	golang.org/x/sync v0.4.0 // indirect
-	golang.org/x/sys v0.13.0 // indirect
-	golang.org/x/term v0.13.0 // indirect
-	golang.org/x/text v0.13.0 // indirect
+	golang.org/x/sync v0.5.0 // indirect
+	golang.org/x/sys v0.14.0 // indirect
+	golang.org/x/term v0.14.0 // indirect
+	golang.org/x/text v0.14.0 // indirect
 	golang.org/x/time v0.3.0 // indirect
 	golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
 	google.golang.org/api v0.126.0 // indirect
@@ -235,7 +238,6 @@ require (
 	gopkg.in/warnings.v0 v0.1.2 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
 	lukechampine.com/frand v1.4.2 // indirect
-	sourcegraph.com/sourcegraph/appdash v0.0.0-20211028080628-e2786a622600 // indirect
 )
 
 replace github.com/hashicorp/terraform-plugin-sdk/v2 => github.com/pulumi/terraform-plugin-sdk/v2 v2.0.0-20230912190043-e6d96b3b8f7e
diff --git a/pf/go.sum b/pf/go.sum
index 2eda3e07..0ae32c6a 100644
--- a/pf/go.sum
+++ b/pf/go.sum
@@ -653,6 +653,8 @@ github.com/d2g/hardwareaddr v0.0.0-20190221164911-e7d9fbe030e4/go.mod h1:bMl4RjI
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/deckarep/golang-set/v2 v2.5.0 h1:hn6cEZtQ0h3J8kFrHR/NrzyOoTnjgW1+FmNJzQ7y/sA=
+github.com/deckarep/golang-set/v2 v2.5.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4=
 github.com/denisenkom/go-mssqldb v0.12.2/go.mod h1:lnIw1mZukFRZDJYQ0Pb833QS2IaC3l5HkEfra2LJ+sk=
 github.com/dennwc/varint v1.0.0/go.mod h1:hnItb35rvZvJrbTALZtY/iQfDs48JKRG1RPpgziApxA=
 github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0=
@@ -1354,15 +1356,15 @@ github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcME
 github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
 github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
 github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
-github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98=
-github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
+github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
+github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
 github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
 github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
 github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
 github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
 github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
-github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
-github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
+github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
+github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
 github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
 github.com/mattn/go-shellwords v1.0.6/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
 github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
@@ -1434,14 +1436,14 @@ github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJ
 github.com/montanaflynn/stats v0.6.6/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
 github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
 github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ=
-github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b h1:1XF24mVaiu7u+CFywTdcDo2ie1pzzhwjt6RHqzpMU34=
-github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b/go.mod h1:fQuZ0gauxyBcmsdE3ZT4NasjaRdxmbCS0jRHsrWu3Ho=
+github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI=
+github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo=
 github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
 github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
 github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
 github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
-github.com/muesli/termenv v0.15.1 h1:UzuTb/+hhlBugQz28rpzey4ZuKcZ03MeKsoG7IJZIxs=
-github.com/muesli/termenv v0.15.1/go.mod h1:HeAQPTzpfs016yGtA4g00CsdYnVLJvxsS4ANqrZs2sQ=
+github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo=
+github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8=
 github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
 github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
 github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
@@ -1542,6 +1544,8 @@ github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrap
 github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
 github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac=
 github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
+github.com/pgavlin/fx v0.1.6 h1:r9jEg69DhNoCd3Xh0+5mIbdbS3PqWrVWujkY76MFRTU=
+github.com/pgavlin/fx v0.1.6/go.mod h1:KWZJ6fqBBSh8GxHYqwYCf3rYE7Gp2p0N8tJp8xv9u9M=
 github.com/pgavlin/goldmark v1.1.33-0.20200616210433-b5eb04559386 h1:LoCV5cscNVWyK5ChN/uCoIFJz8jZD63VQiGJIRgr6uo=
 github.com/pgavlin/goldmark v1.1.33-0.20200616210433-b5eb04559386/go.mod h1:MRxHTJrf9FhdfNQ8Hdeh9gmHevC9RJE/fu8M3JIGjoE=
 github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=
@@ -1625,17 +1629,19 @@ github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1
 github.com/prometheus/prometheus v0.35.0/go.mod h1:7HaLx5kEPKJ0GDgbODG0fZgXbQ8K/XjZNJXQmbmgQlY=
 github.com/prometheus/prometheus v0.37.0/go.mod h1:egARUgz+K93zwqsVIAneFlLZefyGOON44WyAp4Xqbbk=
 github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
-github.com/pulumi/esc v0.6.0 h1:m8jWgBektlj26RvrN3/sM0r1lYiwTMmqVPvLxCxahGE=
-github.com/pulumi/esc v0.6.0/go.mod h1:Y6W21yUukvxS2NnS5ae1beMSPhMvj0xNAYcDqDHVj/g=
+github.com/pulumi/appdash v0.0.0-20231130102222-75f619a67231 h1:vkHw5I/plNdTr435cARxCW6q9gc0S/Yxz7Mkd38pOb0=
+github.com/pulumi/appdash v0.0.0-20231130102222-75f619a67231/go.mod h1:murToZ2N9hNJzewjHBgfFdXhZKjY3z5cYC1VXk+lbFE=
+github.com/pulumi/esc v0.6.1-0.20231111193429-44b746a5b3b5 h1:1DJMji9F7XPea46bSuhy4/85n8J4/Mfz8PWLZtjKKiI=
+github.com/pulumi/esc v0.6.1-0.20231111193429-44b746a5b3b5/go.mod h1:Y6W21yUukvxS2NnS5ae1beMSPhMvj0xNAYcDqDHVj/g=
 github.com/pulumi/pulumi-java/pkg v0.9.8 h1:c8mYsalnRXA2Ibgvv6scefOn6mW1Vb0UT0mcDqjsivQ=
 github.com/pulumi/pulumi-java/pkg v0.9.8/go.mod h1:c6rSw/+q4O0IImgJ9axxoC6QesbPYWBaG5gimbHouUQ=
 github.com/pulumi/pulumi-terraform-bridge/testing v0.0.1 h1:SCg1gjfY9N4yn8U8peIUYATifjoDABkyR7H9lmefsfc=
-github.com/pulumi/pulumi-yaml v1.4.2 h1:LKptNH2lG6f5IjJmR7bvu7ouBE4sgkTRNd4I8ZHmjHw=
-github.com/pulumi/pulumi-yaml v1.4.2/go.mod h1:1hwsXFnT7hm2ZeftY5fzjkngjs2eT/rv9MsPE48Leb4=
-github.com/pulumi/pulumi/pkg/v3 v3.94.2 h1:b+gQ+EJZMYfSIu/sb/+p36VmfZ2ZJWbEWMsptt2f6Kk=
-github.com/pulumi/pulumi/pkg/v3 v3.94.2/go.mod h1:aCeDMlncyDwX3RG6+vYUjWsy6bOEtoHUY0twCcn5qqE=
-github.com/pulumi/pulumi/sdk/v3 v3.94.2 h1:B8W3CCzA2fX/0730o/7KQXdmqrY5KdgGtiAA6rDMVlk=
-github.com/pulumi/pulumi/sdk/v3 v3.94.2/go.mod h1:zeqyIODqbb6GrEyhFV6aJET/xBSXSnF7Bw/EjbYZUnU=
+github.com/pulumi/pulumi-yaml v1.4.3 h1:GO36c7FTl8If20Dn/w2Hi3huP7kmsO00KNaz3GJU0Ws=
+github.com/pulumi/pulumi-yaml v1.4.3/go.mod h1:1hwsXFnT7hm2ZeftY5fzjkngjs2eT/rv9MsPE48Leb4=
+github.com/pulumi/pulumi/pkg/v3 v3.96.2 h1:d+zDsh4K1j90q/upDRkhgxHkspP20Xxd3g6OaMbw574=
+github.com/pulumi/pulumi/pkg/v3 v3.96.2/go.mod h1:lR+q06XrnEqRzJXZ6IeHb4nu+U5fswFcc51Q0MRBTio=
+github.com/pulumi/pulumi/sdk/v3 v3.96.2 h1:q5ZKdf+e9JR+a6Eiueg0Ohy6jCQGk9pO2V7xI/qGP3I=
+github.com/pulumi/pulumi/sdk/v3 v3.96.2/go.mod h1:yvD23IIRiqIXuo4kaZNe5zK/uT0nhO99wr6BVEqoi7A=
 github.com/pulumi/schema-tools v0.1.2 h1:Fd9xvUjgck4NA+7/jSk7InqCUT4Kj940+EcnbQKpfZo=
 github.com/pulumi/schema-tools v0.1.2/go.mod h1:62lgj52Tzq11eqWTIaKd+EVyYAu5dEcDJxMhTjvMO/k=
 github.com/pulumi/terraform-diff-reader v0.0.2 h1:kTE4nEXU3/SYXESvAIem+wyHMI3abqkI3OhJ0G04LLI=
@@ -1976,8 +1982,8 @@ golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0
 golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
 golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
 golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
-golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
-golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
+golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA=
+golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -1988,8 +1994,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
 golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
 golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
-golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
-golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
+golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ=
+golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE=
 golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
 golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
 golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@@ -2020,8 +2026,8 @@ golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
 golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
 golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
 golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
-golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY=
-golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
+golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
+golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
 golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -2107,8 +2113,8 @@ golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
 golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
 golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
 golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
-golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
-golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
+golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg=
+golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -2156,8 +2162,8 @@ golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJ
 golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ=
-golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
+golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
+golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
 golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -2306,8 +2312,8 @@ golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
-golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q=
+golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
@@ -2317,8 +2323,8 @@ golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
 golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
 golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
-golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek=
-golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
+golang.org/x/term v0.14.0 h1:LGK9IlZ8T9jvdy6cTdfKUCltatMFOehAQo9SRC46UQ8=
+golang.org/x/term v0.14.0/go.mod h1:TySc+nGkYR6qt8km8wUhuFRTVSMIX3XPR58y2lC8vww=
 golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -2332,8 +2338,8 @@ golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
 golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
 golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
 golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
-golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
-golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
+golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
+golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
 golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -2433,8 +2439,8 @@ golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E
 golang.org/x/tools v0.1.11/go.mod h1:SgwaegtQh8clINPpECJMqnxLv9I09HLqnW3RMqW0CA4=
 golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
 golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
-golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc=
-golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg=
+golang.org/x/tools v0.15.0 h1:zdAyfUGbYmuVokhzVmghFl2ZJh5QhcfebBgmVPFYA+8=
+golang.org/x/tools v0.15.0/go.mod h1:hpksKq4dtpQWS1uQ61JkdqWM3LscIS6Slf+VVkm+wQk=
 golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -2846,5 +2852,3 @@ sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
 sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
 sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
 sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU=
-sourcegraph.com/sourcegraph/appdash v0.0.0-20211028080628-e2786a622600 h1:hfyJ5ku9yFtLVOiSxa3IN+dx5eBQT9mPmKFypAmg8XM=
-sourcegraph.com/sourcegraph/appdash v0.0.0-20211028080628-e2786a622600/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU=
diff --git a/pf/internal/logging/logging.go b/pf/internal/logging/logging.go
new file mode 100644
index 00000000..8437c6c6
--- /dev/null
+++ b/pf/internal/logging/logging.go
@@ -0,0 +1,221 @@
+// Copyright 2016-2023, Pulumi Corporation.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package logging
+
+import (
+	"context"
+	"io"
+	"os"
+	"regexp"
+	"strings"
+
+	"github.com/hashicorp/go-hclog"
+	"github.com/hashicorp/terraform-plugin-log/tflog"
+
+	"github.com/pulumi/pulumi/sdk/v3/go/common/diag"
+	"github.com/pulumi/pulumi/sdk/v3/go/common/resource"
+
+	"github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tfbridge"
+	"github.com/pulumi/pulumi-terraform-bridge/v3/unstable/logging"
+)
+
+type LogSink = logging.Sink
+
+// Sets up Context-scoped loggers to route Terraform logs to the Pulumi CLI process so they are visible to the user.
+//
+// Log verbosity is controlled by the TF_LOG environment variable (set to TRACE, DEBUG, INFO, WARN, ERROR or OFF). By
+// default, INFO-level logs are emitted.
+//
+// See also:
+//
+// - https://developer.hashicorp.com/terraform/plugin/log/writing
+// - https://www.pulumi.com/docs/support/troubleshooting
+func InitLogging(ctx context.Context, opts LogOptions) context.Context {
+	ctx = setupRootLoggers(ctx, newLogSinkWriter(ctx, opts.LogSink))
+
+	if opts.URN != "" {
+		ctx = tflog.SetField(ctx, "urn", string(opts.URN))
+	}
+
+	if opts.ProviderName != "" {
+		p := opts.ProviderName
+		if opts.ProviderVersion != "" {
+			p += "@" + opts.ProviderVersion
+		}
+		ctx = tflog.SetField(ctx, "provider", p)
+	}
+
+	return context.WithValue(ctx, logging.CtxKey,
+		logging.NewHost(ctx, opts.LogSink, opts.URN,
+			func(l *logging.Host[tfbridge.Log]) tfbridge.Log {
+				return l
+			}))
+}
+
+// See InitLogging.
+type LogOptions struct {
+	LogSink         LogSink
+	ProviderName    string
+	ProviderVersion string
+	URN             resource.URN
+}
+
+// Directs any logs written using the tflog API in the given Context to the given output.
+func setupRootLoggers(ctx context.Context, output io.Writer) context.Context {
+	desiredLevel := parseTfLogEnvVar()
+	sdkLoggerOptions := makeLoggerOptions("sdk", desiredLevel, output)
+	ctx = context.WithValue(ctx, sdkKey, hclog.New(sdkLoggerOptions))
+	ctx = context.WithValue(ctx, sdkOptionsKey, sdkLoggerOptions)
+	providerLoggerOptions := makeLoggerOptions("provider", desiredLevel, output)
+	ctx = context.WithValue(ctx, providerKey, hclog.New(providerLoggerOptions))
+	ctx = context.WithValue(ctx, providerOptionsKey, providerLoggerOptions)
+	return ctx
+}
+
+// Choose the default level carefully: logs at this level or higher (more severe) will be shown to the user of Pulumi
+// CLI direcrtly by default. Experimentally it seems that Info is too verbose, for example Cloudflare provider emits
+// routine authentication messages at INFO level.
+func defaultTFLogLevel() hclog.Level {
+	return hclog.Warn
+}
+
+func makeLoggerOptions(name string, level hclog.Level, output io.Writer) *hclog.LoggerOptions {
+	if level == hclog.NoLevel {
+		level = defaultTFLogLevel()
+	}
+	return &hclog.LoggerOptions{
+		Name:              name,
+		Output:            output,
+		Level:             level,
+		IndependentLevels: true,
+		IncludeLocation:   true,
+		TimeFormat:        " ",
+
+		// Empirically the value of 1 seems to work in the current Pulumi setup, @caller field now points to the
+		// file where the logging originates.
+		AdditionalLocationOffset: 1,
+	}
+}
+
+// Re-interprets structured JSON logs as calls against LogSink. To be used with SetupRootLoggers.
+func newLogSinkWriter(ctx context.Context, sink LogSink) io.Writer {
+	desiredLevel := parseTfLogEnvVar()
+	return &logSinkWriter{
+		desiredLevel: desiredLevel,
+		ctx:          ctx,
+		sink:         sink,
+	}
+}
+
+type logSinkWriter struct {
+	desiredLevel hclog.Level
+	ctx          context.Context
+	sink         LogSink
+}
+
+var _ io.Writer = &logSinkWriter{}
+
+func (w *logSinkWriter) Write(p []byte) (n int, err error) {
+	n = len(p)
+	if w.sink == nil {
+		return
+	}
+
+	raw := string(p)
+	level, raw := parseLevelFromRawString(raw)
+
+	if level <= w.desiredLevel {
+		return
+	}
+
+	urn, raw := parseUrnFromRawString(raw)
+	severity := logLevelToSeverity(level)
+
+	err = w.sink.Log(w.ctx, severity, urn, raw)
+	return
+}
+
+func parseTfLogEnvVar() hclog.Level {
+	env, present := os.LookupEnv("TF_LOG")
+	if !present {
+		return hclog.NoLevel
+	}
+	switch strings.ToUpper(env) {
+	case "ERROR":
+		return hclog.Error
+	case "WARN":
+		return hclog.Warn
+	case "INFO":
+		return hclog.Info
+	case "TRACE":
+		return hclog.Trace
+	case "DEBUG":
+		return hclog.Debug
+	case "OFF":
+		return hclog.Off
+	default:
+		return hclog.NoLevel
+	}
+}
+
+var quotedUrnPattern = regexp.MustCompile(`[ ]urn=["]([^"]+)["]`)
+var bareUrnPattern = regexp.MustCompile(`[ ]urn=([^ ]+)`)
+
+func parseUrnFromRawString(s string) (resource.URN, string) {
+	if ok := quotedUrnPattern.FindStringSubmatch(s); len(ok) > 0 {
+		return resource.URN(ok[1]), strings.Replace(s, ok[0], "", 1)
+	}
+	if ok := bareUrnPattern.FindStringSubmatch(s); len(ok) > 0 {
+		return resource.URN(ok[1]), strings.Replace(s, ok[0], "", 1)
+	}
+	return "", s
+}
+
+func parseLevelFromRawString(s string) (hclog.Level, string) {
+	i := strings.Index(s, "[")
+	j := strings.Index(s[i:], "]")
+	remainder := s[0:i] + s[i+j+2:]
+	switch s[i : i+j+1] {
+	case "[ERROR]":
+		return hclog.Error, remainder
+	case "[WARN]":
+		return hclog.Warn, remainder
+	case "[DEBUG]":
+		return hclog.Debug, remainder
+	case "[INFO]":
+		return hclog.Info, remainder
+	case "[TRACE]":
+		return hclog.Trace, remainder
+	default:
+		return hclog.Trace, remainder
+	}
+}
+
+func logLevelToSeverity(l hclog.Level) diag.Severity {
+	switch l {
+	case hclog.Error:
+		return diag.Error
+	case hclog.Warn:
+		return diag.Warning
+	case hclog.Info:
+		return diag.Info
+	case hclog.Debug:
+		return diag.Debug
+	case hclog.Trace:
+		return diag.Debug
+	default:
+		return diag.Debug
+	}
+}
diff --git a/unstable/logging/recover_keys.go b/pf/internal/logging/recover_keys.go
similarity index 100%
rename from unstable/logging/recover_keys.go
rename to pf/internal/logging/recover_keys.go
diff --git a/unstable/logging/recover_keys_test.go b/pf/internal/logging/recover_keys_test.go
similarity index 100%
rename from unstable/logging/recover_keys_test.go
rename to pf/internal/logging/recover_keys_test.go
diff --git a/pf/internal/plugin/provider_context.go b/pf/internal/plugin/provider_context.go
index 98ad190b..0e364225 100644
--- a/pf/internal/plugin/provider_context.go
+++ b/pf/internal/plugin/provider_context.go
@@ -154,9 +154,10 @@ func (prov *provider) Delete(
 		timeout)
 }
 
-func (prov *provider) Construct(info plugin.ConstructInfo, typ tokens.Type, name tokens.QName, parent resource.URN,
-	inputs resource.PropertyMap, options plugin.ConstructOptions) (plugin.ConstructResult, error) {
-	return prov.ProviderWithContext.ConstructWithContext(prov.ctx, info, typ, name, parent, inputs, options)
+func (prov *provider) Construct(info plugin.ConstructInfo, typ tokens.Type, name string, parent resource.URN,
+	inputs resource.PropertyMap, options plugin.ConstructOptions,
+) (plugin.ConstructResult, error) {
+	return prov.ProviderWithContext.ConstructWithContext(prov.ctx, info, typ, tokens.QName(name), parent, inputs, options)
 }
 
 func (prov *provider) Invoke(tok tokens.ModuleMember,
diff --git a/pf/tfbridge/logging.go b/pf/tfbridge/logging.go
index 3c69b406..701aec31 100644
--- a/pf/tfbridge/logging.go
+++ b/pf/tfbridge/logging.go
@@ -19,14 +19,14 @@ import (
 
 	"github.com/pulumi/pulumi/sdk/v3/go/common/resource"
 
-	"github.com/pulumi/pulumi-terraform-bridge/v3/unstable/logging"
+	logutils "github.com/pulumi/pulumi-terraform-bridge/pf/internal/logging"
 )
 
 // Configures logging. Note that urn is optional but useful to identify logs with resources.
 //
 // See https://developer.hashicorp.com/terraform/plugin/log/writing
-func (p *provider) initLogging(ctx context.Context, sink logging.Sink, urn resource.URN) context.Context {
-	return logging.InitLogging(ctx, logging.LogOptions{
+func (p *provider) initLogging(ctx context.Context, sink logutils.LogSink, urn resource.URN) context.Context {
+	return logutils.InitLogging(ctx, logutils.LogOptions{
 		LogSink:         sink,
 		URN:             urn,
 		ProviderName:    p.info.Name,
diff --git a/pf/tfbridge/provider.go b/pf/tfbridge/provider.go
index 3b607ed7..caca2ca2 100644
--- a/pf/tfbridge/provider.go
+++ b/pf/tfbridge/provider.go
@@ -35,12 +35,12 @@ import (
 	pulumirpc "github.com/pulumi/pulumi/sdk/v3/proto/go"
 
 	"github.com/pulumi/pulumi-terraform-bridge/pf/internal/convert"
+	logutils "github.com/pulumi/pulumi-terraform-bridge/pf/internal/logging"
 	"github.com/pulumi/pulumi-terraform-bridge/pf/internal/pfutils"
 	pl "github.com/pulumi/pulumi-terraform-bridge/pf/internal/plugin"
 	"github.com/pulumi/pulumi-terraform-bridge/pf/internal/schemashim"
 	"github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tfbridge"
 	shim "github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tfshim"
-	"github.com/pulumi/pulumi-terraform-bridge/v3/unstable/logging"
 )
 
 // Provider implements the Pulumi resource provider operations for any
@@ -60,7 +60,7 @@ type provider struct {
 	configEncoder convert.Encoder
 	configType    tftypes.Object
 	version       semver.Version
-	logSink       logging.Sink
+	logSink       logutils.LogSink
 
 	// Used by CheckConfig to remember the current Provider configuration so that it can be recalled and used for
 	// populating defaults specified via DefaultInfo.Config.
@@ -170,7 +170,7 @@ func newProviderWithContext(ctx context.Context, info tfbridge.ProviderInfo,
 // Internal. The signature of this function can change between major releases. Exposed to facilitate testing.
 func NewProviderServer(
 	ctx context.Context,
-	logSink logging.Sink,
+	logSink logutils.LogSink,
 	info tfbridge.ProviderInfo,
 	meta ProviderMetadata,
 ) (pulumirpc.ResourceProviderServer, error) {
diff --git a/pkg/tfbridge/check_failures.go b/pkg/tfbridge/check_failures.go
index b430d9ea..136657f5 100644
--- a/pkg/tfbridge/check_failures.go
+++ b/pkg/tfbridge/check_failures.go
@@ -22,7 +22,7 @@ import (
 	"github.com/pulumi/pulumi/sdk/v3/go/common/resource"
 	"github.com/pulumi/pulumi/sdk/v3/go/common/resource/plugin"
 
-	"github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tfshim"
+	shim "github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tfshim"
 	"github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tfshim/walk"
 )
 
@@ -156,7 +156,7 @@ func NewCheckFailure(
 		return formatProviderCheckFailure(reasonType, reason, pp, urn, configPrefix, schemaMap, schemaInfos)
 	}
 	if pp != nil && pp.valuePath != "" {
-		reason = fmt.Sprintf("%s. Examine values at '%s.%s'.", reason, urn.Name().String(), pp.valuePath)
+		reason = fmt.Sprintf("%s. Examine values at '%s.%s'.", reason, urn.Name(), pp.valuePath)
 	}
 	return plugin.CheckFailure{
 		Reason: reason,
@@ -177,7 +177,7 @@ func formatProviderCheckFailure(
 	reason = "could not validate provider configuration: " + reason
 	if isExplicitProvider(urn) {
 		if pp != nil && pp.valuePath != "" {
-			reason = fmt.Sprintf("%s. Examine values at '%s.%s'.", reason, urn.Name().String(),
+			reason = fmt.Sprintf("%s. Examine values at '%s.%s'.", reason, urn.Name(),
 				pp.valuePath)
 		}
 		// Similarly to normal resources, do not populate Property here as that changes the UX of how it
@@ -282,7 +282,7 @@ func pulumiConfigExpr(configPrefix string, pp CheckFailurePath) string {
 // Provider configuration can be using an explicit provider or the default provider, use a heuristic here based on URN,
 // to detect the default provider.
 func isExplicitProvider(urn resource.URN) bool {
-	return urn != "" && !strings.HasPrefix(urn.Name().String(), "default")
+	return urn != "" && !strings.HasPrefix(urn.Name(), "default")
 }
 
 func missingProviderKey(
diff --git a/pkg/tfbridge/diff.go b/pkg/tfbridge/diff.go
index 6c534e6f..f3abc6e8 100644
--- a/pkg/tfbridge/diff.go
+++ b/pkg/tfbridge/diff.go
@@ -24,6 +24,7 @@ import (
 	pulumirpc "github.com/pulumi/pulumi/sdk/v3/proto/go"
 
 	shim "github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tfshim"
+	shimutil "github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tfshim/util"
 )
 
 // containsComputedValues returns true if the given property value is or contains a computed value.
@@ -102,7 +103,7 @@ func visitPropertyValue(
 				// fill in default values for empty fields (note that this is a property of the field reader, not of
 				// the schema) as it does when computing the hash code for a set element.
 				ctx := &conversionContext{Ctx: ctx}
-				ev, err := ctx.MakeTerraformInput(ep, resource.PropertyValue{}, e, etfs, eps, rawNames)
+				ev, err := ctx.makeTerraformInput(ep, resource.PropertyValue{}, e, etfs, eps, rawNames)
 				if err != nil {
 					return
 				}
@@ -140,7 +141,7 @@ func visitPropertyValue(
 			psflds = ps.Fields
 		}
 
-		rawElementNames := rawNames || useRawNames(tfs)
+		rawElementNames := rawNames || shimutil.IsOfTypeMap(tfs)
 		for k, e := range v.ObjectValue() {
 			var elementPath string
 			if strings.ContainsAny(string(k), `."[]`) {
@@ -261,11 +262,11 @@ func doIgnoreChanges(ctx context.Context, tfs shim.SchemaMap, ps map[string]*Sch
 	}
 	for k, v := range olds {
 		en, etf, eps := getInfoFromPulumiName(k, tfs, ps, false)
-		visitPropertyValue(ctx, en, string(k), v, etf, eps, useRawNames(etf), visitor)
+		visitPropertyValue(ctx, en, string(k), v, etf, eps, shimutil.IsOfTypeMap(etf), visitor)
 	}
 	for k, v := range news {
 		en, etf, eps := getInfoFromPulumiName(k, tfs, ps, false)
-		visitPropertyValue(ctx, en, string(k), v, etf, eps, useRawNames(etf), visitor)
+		visitPropertyValue(ctx, en, string(k), v, etf, eps, shimutil.IsOfTypeMap(etf), visitor)
 	}
 
 	tfDiff.IgnoreChanges(ignoredKeySet)
@@ -298,15 +299,15 @@ func makeDetailedDiff(
 	diff := map[string]*pulumirpc.PropertyDiff{}
 	for k, v := range olds {
 		en, etf, eps := getInfoFromPulumiName(k, tfs, ps, false)
-		makePropertyDiff(ctx, en, string(k), v, tfDiff, diff, forceDiff, etf, eps, false, useRawNames(etf))
+		makePropertyDiff(ctx, en, string(k), v, tfDiff, diff, forceDiff, etf, eps, false, shimutil.IsOfTypeMap(etf))
 	}
 	for k, v := range news {
 		en, etf, eps := getInfoFromPulumiName(k, tfs, ps, false)
-		makePropertyDiff(ctx, en, string(k), v, tfDiff, diff, forceDiff, etf, eps, false, useRawNames(etf))
+		makePropertyDiff(ctx, en, string(k), v, tfDiff, diff, forceDiff, etf, eps, false, shimutil.IsOfTypeMap(etf))
 	}
 	for k, v := range olds {
 		en, etf, eps := getInfoFromPulumiName(k, tfs, ps, false)
-		makePropertyDiff(ctx, en, string(k), v, tfDiff, diff, forceDiff, etf, eps, true, useRawNames(etf))
+		makePropertyDiff(ctx, en, string(k), v, tfDiff, diff, forceDiff, etf, eps, true, shimutil.IsOfTypeMap(etf))
 	}
 
 	changes := pulumirpc.DiffResponse_DIFF_NONE
diff --git a/pkg/tfbridge/info.go b/pkg/tfbridge/info.go
index 4b90b28e..966b6d68 100644
--- a/pkg/tfbridge/info.go
+++ b/pkg/tfbridge/info.go
@@ -161,9 +161,11 @@ type Log interface {
 
 // Get access to the [Logger] associated with this context.
 func GetLogger(ctx context.Context) Logger {
-	logger := ctx.Value(logging.CtxKey)
-	contract.Assertf(logger != nil, "Cannot call GetLogger on a context that is not equipped with a Logger")
-	return newLoggerAdapter(logger)
+	logger, ok := ctx.Value(logging.CtxKey).(Logger)
+	if !ok {
+		panic("Cannot call GetLogger on a context that is not equipped with a Logger")
+	}
+	return logger
 }
 
 func (info *ProviderInfo) GetConfig() map[string]*SchemaInfo {
@@ -416,6 +418,10 @@ type SchemaInfo struct {
 	CSharpName string
 
 	// a type to override the default; "" uses the default.
+	//
+	// If the type overridden is an object type, `Type: newName` is interpreted as a
+	// move operation, pointing the property to a type called `newName` and creating
+	// `newName` with the schema described by this type.
 	Type tokens.Type
 
 	// alternative types that can be used instead of the override.
@@ -424,7 +430,7 @@ type SchemaInfo struct {
 	// a type to override when the property is a nested structure.
 	NestedType tokens.Type
 
-	// an optional idemponent transformation, applied before passing to TF.
+	// an optional idempotent transformation, applied before passing to TF.
 	Transform Transformer
 
 	// a schema override for elements for arrays, maps, and sets.
@@ -1037,8 +1043,9 @@ func (m *MarshallableDefaultInfo) Unmarshal() *DefaultInfo {
 
 // MarshallableResourceInfo is the JSON-marshallable form of a Pulumi ResourceInfo value.
 type MarshallableResourceInfo struct {
-	Tok    tokens.Type                        `json:"tok"`
-	Fields map[string]*MarshallableSchemaInfo `json:"fields"`
+	Tok        tokens.Type                        `json:"tok"`
+	CSharpName string                             `json:"csharpName,omitempty"`
+	Fields     map[string]*MarshallableSchemaInfo `json:"fields"`
 
 	// Deprecated: IDFields is not currently used and will be deprecated in the next major version of
 	// pulumi-terraform-bridge.
@@ -1052,9 +1059,10 @@ func MarshalResourceInfo(r *ResourceInfo) *MarshallableResourceInfo {
 		fields[k] = MarshalSchemaInfo(v)
 	}
 	return &MarshallableResourceInfo{
-		Tok:      r.Tok,
-		Fields:   fields,
-		IDFields: r.IDFields,
+		Tok:        r.Tok,
+		CSharpName: r.CSharpName,
+		Fields:     fields,
+		IDFields:   r.IDFields,
 	}
 }
 
@@ -1065,9 +1073,10 @@ func (m *MarshallableResourceInfo) Unmarshal() *ResourceInfo {
 		fields[k] = v.Unmarshal()
 	}
 	return &ResourceInfo{
-		Tok:      m.Tok,
-		Fields:   fields,
-		IDFields: m.IDFields,
+		Tok:        m.Tok,
+		Fields:     fields,
+		IDFields:   m.IDFields,
+		CSharpName: m.CSharpName,
 	}
 }
 
diff --git a/pkg/tfbridge/log.go b/pkg/tfbridge/log.go
index 2cb5fb1e..99445e64 100644
--- a/pkg/tfbridge/log.go
+++ b/pkg/tfbridge/log.go
@@ -21,7 +21,10 @@ import (
 
 	"github.com/pulumi/pulumi/pkg/v3/resource/provider"
 	"github.com/pulumi/pulumi/sdk/v3/go/common/diag"
+	"github.com/pulumi/pulumi/sdk/v3/go/common/resource"
 	"github.com/pulumi/pulumi/sdk/v3/go/common/util/contract"
+
+	"github.com/pulumi/pulumi-terraform-bridge/v3/unstable/logging"
 )
 
 // LogRedirector creates a new redirection writer that takes as input plugin stderr output, and routes it to the
@@ -115,28 +118,12 @@ func (lr *LogRedirector) Write(p []byte) (n int, err error) {
 	return written, nil
 }
 
-type loggerAdapter struct {
-	Log
-	untyped untypedLogger
-}
-
-func (a *loggerAdapter) Status() Log {
-	return a.untyped.StatusUntyped().(Log)
-}
-
-var _ Logger = (*loggerAdapter)(nil)
-
-type untypedLogger interface {
-	Log
-	StatusUntyped() any
-}
-
-func newLoggerAdapter(logger any) Logger {
-	uLogger, ok := logger.(untypedLogger)
-	contract.Assertf(ok, "Context carries a logger that does not implement UntypedLogger")
-
-	return &loggerAdapter{
-		Log:     uLogger,
-		untyped: uLogger,
-	}
+func ctxWithHostLogger(
+	ctx context.Context, host *provider.HostClient, urn resource.URN,
+) context.Context {
+	return context.WithValue(ctx, logging.CtxKey,
+		logging.NewHost(ctx, host, urn,
+			func(l *logging.Host[Log]) Log {
+				return l
+			}))
 }
diff --git a/pkg/tfbridge/names.go b/pkg/tfbridge/names.go
index 8597aa27..81b32f09 100644
--- a/pkg/tfbridge/names.go
+++ b/pkg/tfbridge/names.go
@@ -364,7 +364,7 @@ func ComputeAutoNameDefault(
 	}
 
 	// Take the URN name part, transform it if required, and then append some unique characters if requested.
-	vs := string(defaultOptions.URN.Name())
+	vs := defaultOptions.URN.Name()
 	if options.Transform != nil {
 		vs = options.Transform(vs)
 	}
diff --git a/pkg/tfbridge/provider.go b/pkg/tfbridge/provider.go
index 163a5cb3..2ef0af34 100644
--- a/pkg/tfbridge/provider.go
+++ b/pkg/tfbridge/provider.go
@@ -34,6 +34,7 @@ import (
 	"golang.org/x/net/context"
 	"google.golang.org/grpc/codes"
 
+	"github.com/pulumi/pulumi-terraform-bridge/v3/unstable/propertyvalue"
 	"github.com/pulumi/pulumi/pkg/v3/resource/provider"
 	"github.com/pulumi/pulumi/sdk/v3/go/common/diag"
 	"github.com/pulumi/pulumi/sdk/v3/go/common/resource"
@@ -44,7 +45,6 @@ import (
 	pulumirpc "github.com/pulumi/pulumi/sdk/v3/proto/go"
 
 	shim "github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tfshim"
-	"github.com/pulumi/pulumi-terraform-bridge/v3/unstable/logging"
 )
 
 // Provider implements the Pulumi resource provider operations for any Terraform plugin.
@@ -157,7 +157,7 @@ func NewProvider(ctx context.Context, host *provider.HostClient, module string,
 		host:         host,
 		module:       module,
 		version:      version,
-		tf:           shim.NewProviderWithContext(tf),
+		tf:           tf,
 		info:         info,
 		config:       tf.Schema(),
 		pulumiSchema: pulumiSchema,
@@ -187,38 +187,19 @@ func (p *Provider) Attach(context context.Context, req *pulumirpc.PluginAttach)
 }
 
 func (p *Provider) loggingContext(ctx context.Context, urn resource.URN) context.Context {
-	// There is no host in a testing context.
-	if p.host == nil {
-		// For tests that did not call InitLogging yet, we should call it here so that
-		// GetLogger does not panic.
-		if ctx.Value(logging.CtxKey) == nil {
-			return logging.InitLogging(ctx, logging.LogOptions{
-				URN:             urn,
-				ProviderName:    p.info.Name,
-				ProviderVersion: p.version,
-			})
-		}
-
-		// Otherwise keep the context as-is.
-		return ctx
+	if p.host != nil {
+		log.SetOutput(&LogRedirector{
+			writers: map[string]func(string) error{
+				tfTracePrefix: func(msg string) error { return p.host.Log(ctx, diag.Debug, "", msg) },
+				tfDebugPrefix: func(msg string) error { return p.host.Log(ctx, diag.Debug, "", msg) },
+				tfInfoPrefix:  func(msg string) error { return p.host.Log(ctx, diag.Info, "", msg) },
+				tfWarnPrefix:  func(msg string) error { return p.host.Log(ctx, diag.Warning, "", msg) },
+				tfErrorPrefix: func(msg string) error { return p.host.Log(ctx, diag.Error, "", msg) },
+			},
+		})
 	}
 
-	log.SetOutput(&LogRedirector{
-		writers: map[string]func(string) error{
-			tfTracePrefix: func(msg string) error { return p.host.Log(ctx, diag.Debug, "", msg) },
-			tfDebugPrefix: func(msg string) error { return p.host.Log(ctx, diag.Debug, "", msg) },
-			tfInfoPrefix:  func(msg string) error { return p.host.Log(ctx, diag.Info, "", msg) },
-			tfWarnPrefix:  func(msg string) error { return p.host.Log(ctx, diag.Warning, "", msg) },
-			tfErrorPrefix: func(msg string) error { return p.host.Log(ctx, diag.Error, "", msg) },
-		},
-	})
-
-	return logging.InitLogging(ctx, logging.LogOptions{
-		LogSink:         p.host,
-		URN:             urn,
-		ProviderName:    p.info.Name,
-		ProviderVersion: p.version,
-	})
+	return ctxWithHostLogger(ctx, p.host, urn)
 }
 
 func (p *Provider) label() string {
@@ -566,7 +547,7 @@ func (p *Provider) Configure(ctx context.Context,
 		p.supportsSecrets = true
 	}
 
-	ctx = p.loggingContext(ctx, "")
+	p.loggingContext(ctx, "")
 
 	configEnc := NewConfigEncoding(p.config, p.info.Config)
 
@@ -593,7 +574,7 @@ func (p *Provider) Configure(ctx context.Context,
 	}
 
 	// Now actually attempt to do the configuring and return its resulting error (if any).
-	if err = p.tfc().ConfigureWithContext(ctx, config); err != nil {
+	if err = p.tf.Configure(config); err != nil {
 		replacedErr, replacementError := ReplaceConfigProperties(err.Error(), p.info.Config, p.config)
 		if replacementError != nil {
 			wrappedErr := errors.Wrapf(
@@ -698,6 +679,31 @@ func (p *Provider) Check(ctx context.Context, req *pulumirpc.CheckRequest) (*pul
 	return &pulumirpc.CheckResponse{Inputs: minputs, Failures: failures}, nil
 }
 
+// For properties with MaxItemsOne, where the state is still an array
+// (i.e. from a previous version without MaxItemsOne)
+// we need to mark them for update manually in order to correct the state
+// from an array to a flat type.
+// The diff is otherwise ignored since MakeTerraformInputs won't touch
+// the type if it in the right shape.
+func markWronglyTypedMaxItemsOneStateDiff(
+	schema shim.SchemaMap, info map[string]*SchemaInfo, olds resource.PropertyMap,
+) bool {
+	res := False()
+	tr := func(pulumiPath resource.PropertyPath, localValue resource.PropertyValue) (resource.PropertyValue, error) {
+		schemaPath := PropertyPathToSchemaPath(pulumiPath, schema, info)
+		localSchema, info, err := LookupSchemas(schemaPath, schema, info)
+		contract.IgnoreError(err)
+		if IsMaxItemsOne(localSchema, info) && localValue.IsArray() {
+			glog.V(9).Infof("Found type mismatch for %s, flagging for update.", pulumiPath)
+			*res = true
+		}
+		return localValue, nil // don't change just visit
+	}
+	_, err := propertyvalue.TransformPropertyValue(make(resource.PropertyPath, 0), tr, resource.NewObjectProperty(olds))
+	contract.AssertNoErrorf(err, "markWronglyTypedMaxItemsOneStateDiff should not return errors!")
+	return *res
+}
+
 // Diff checks what impacts a hypothetical update will have on the resource's properties.
 func (p *Provider) Diff(ctx context.Context, req *pulumirpc.DiffRequest) (*pulumirpc.DiffResponse, error) {
 	ctx = p.loggingContext(ctx, resource.URN(req.GetUrn()))
@@ -743,7 +749,7 @@ func (p *Provider) Diff(ctx context.Context, req *pulumirpc.DiffRequest) (*pulum
 		return nil, errors.Wrapf(err, "preparing %s's new property state", urn)
 	}
 
-	diff, err := p.tfc().DiffWithContext(ctx, res.TFName, state, config)
+	diff, err := p.tf.Diff(res.TFName, state, config)
 	if err != nil {
 		return nil, errors.Wrapf(err, "diffing %s", urn)
 	}
@@ -819,6 +825,12 @@ func (p *Provider) Diff(ctx context.Context, req *pulumirpc.DiffRequest) (*pulum
 		changes = pulumirpc.DiffResponse_DIFF_SOME
 	}
 
+	if changes == pulumirpc.DiffResponse_DIFF_NONE &&
+		markWronglyTypedMaxItemsOneStateDiff(res.TF.Schema(), res.Schema.Fields, olds) {
+
+		changes = pulumirpc.DiffResponse_DIFF_SOME
+	}
+
 	return &pulumirpc.DiffResponse{
 		Changes:             changes,
 		Replaces:            replaces,
@@ -859,7 +871,7 @@ func (p *Provider) Create(ctx context.Context, req *pulumirpc.CreateRequest) (*p
 		return nil, errors.Wrapf(err, "preparing %s's new property state", urn)
 	}
 
-	diff, err := p.tfc().DiffWithContext(ctx, res.TFName, nil, config)
+	diff, err := p.tf.Diff(res.TFName, nil, config)
 	if err != nil {
 		return nil, errors.Wrapf(err, "diffing %s", urn)
 	}
@@ -881,7 +893,7 @@ func (p *Provider) Create(ctx context.Context, req *pulumirpc.CreateRequest) (*p
 	var newstate shim.InstanceState
 	var reasons []string
 	if !req.GetPreview() {
-		newstate, err = p.tfc().ApplyWithContext(ctx, res.TFName, nil, diff)
+		newstate, err = p.tf.Apply(res.TFName, nil, diff)
 		if newstate == nil {
 			if err == nil {
 				return nil, fmt.Errorf("expected non-nil error with nil state during Create of %s", urn)
@@ -987,7 +999,7 @@ func (p *Provider) Read(ctx context.Context, req *pulumirpc.ReadRequest) (*pulum
 		return nil, errors.Wrapf(err, "preparing %s's new property state", urn)
 	}
 
-	newstate, err := p.tfc().RefreshWithContext(ctx, res.TFName, state, config)
+	newstate, err := p.tf.Refresh(res.TFName, state, config)
 	if err != nil {
 		return nil, errors.Wrapf(err, "refreshing %s", urn)
 	}
@@ -1081,7 +1093,7 @@ func (p *Provider) Update(ctx context.Context, req *pulumirpc.UpdateRequest) (*p
 		return nil, errors.Wrapf(err, "preparing %s's new property state", urn)
 	}
 
-	diff, err := p.tfc().DiffWithContext(ctx, res.TFName, state, config)
+	diff, err := p.tf.Diff(res.TFName, state, config)
 	if err != nil {
 		return nil, errors.Wrapf(err, "diffing %s", urn)
 	}
@@ -1110,7 +1122,7 @@ func (p *Provider) Update(ctx context.Context, req *pulumirpc.UpdateRequest) (*p
 	var newstate shim.InstanceState
 	var reasons []string
 	if !req.GetPreview() {
-		newstate, err = p.tfc().ApplyWithContext(ctx, res.TFName, state, diff)
+		newstate, err = p.tf.Apply(res.TFName, state, diff)
 		if newstate == nil {
 			if err != nil {
 				return nil, err
@@ -1192,7 +1204,7 @@ func (p *Provider) Delete(ctx context.Context, req *pulumirpc.DeleteRequest) (*p
 		diff.SetTimeout(req.Timeout, shim.TimeoutDelete)
 	}
 
-	if _, err := p.tfc().ApplyWithContext(ctx, res.TFName, state, diff); err != nil {
+	if _, err := p.tf.Apply(res.TFName, state, diff); err != nil {
 		return nil, errors.Wrapf(err, "deleting %s", urn)
 	}
 	return &pbempty.Empty{}, nil
@@ -1267,12 +1279,12 @@ func (p *Provider) Invoke(ctx context.Context, req *pulumirpc.InvokeRequest) (*p
 	// If there are no failures in verification, go ahead and perform the invocation.
 	var ret *pbstruct.Struct
 	if len(failures) == 0 {
-		diff, err := p.tfc().ReadDataDiffWithContext(ctx, tfname, rescfg)
+		diff, err := p.tf.ReadDataDiff(tfname, rescfg)
 		if err != nil {
 			return nil, errors.Wrapf(err, "reading data source diff for %s", tok)
 		}
 
-		invoke, err := p.tfc().ReadDataApplyWithContext(ctx, tfname, diff)
+		invoke, err := p.tf.ReadDataApply(tfname, diff)
 		if err != nil {
 			return nil, errors.Wrapf(err, "invoking %s", tok)
 		}
@@ -1355,10 +1367,6 @@ func (p *Provider) GetMapping(
 	return &pulumirpc.GetMappingResponse{}, nil
 }
 
-func (p *Provider) tfc() shim.ProviderWithContext {
-	return shim.NewProviderWithContext(p.tf)
-}
-
 func initializationError(id string, props *pbstruct.Struct, reasons []string) error {
 	contract.Assertf(len(reasons) > 0, "initializationError must be passed at least one reason")
 	detail := pulumirpc.ErrorResourceInitFailed{
diff --git a/pkg/tfbridge/schema.go b/pkg/tfbridge/schema.go
index 515b0878..823488d4 100644
--- a/pkg/tfbridge/schema.go
+++ b/pkg/tfbridge/schema.go
@@ -27,11 +27,14 @@ import (
 	"github.com/golang/glog"
 	pbstruct "github.com/golang/protobuf/ptypes/struct"
 	"github.com/pkg/errors"
-	shim "github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tfshim"
-	"github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tfshim/schema"
+
 	"github.com/pulumi/pulumi/sdk/v3/go/common/resource"
 	"github.com/pulumi/pulumi/sdk/v3/go/common/resource/plugin"
 	"github.com/pulumi/pulumi/sdk/v3/go/common/util/contract"
+
+	shim "github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tfshim"
+	"github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tfshim/schema"
+	shimutil "github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tfshim/util"
 )
 
 // This file deals with translating between the Pulumi representations of a resource's configuration and state and the
@@ -314,19 +317,24 @@ func MakeTerraformInputs(
 		ApplyDefaults:         true,
 		Assets:                AssetTable{},
 	}
-	inputs, err := cctx.MakeTerraformInputs(olds, news, tfs, ps, false)
+	inputs, err := cctx.makeTerraformInputs(olds, news, tfs, ps)
 	if err != nil {
 		return nil, nil, err
 	}
 	return inputs, cctx.Assets, err
 }
 
-// MakeTerraformInput takes a single property plus custom schema info and does whatever is necessary to prepare it for
-// use by Terraform.  Note that this function may have side effects, for instance if it is necessary to spill an asset
-// to disk in order to create a name out of it.  Please take care not to call it superfluously!
-func (ctx *conversionContext) MakeTerraformInput(name string, old, v resource.PropertyValue,
-	tfs shim.Schema, ps *SchemaInfo, rawNames bool) (interface{}, error) {
-
+// makeTerraformInput takes a single property plus custom schema info and does whatever is necessary
+// to prepare it for use by Terraform. Note that this function may have side effects, for instance
+// if it is necessary to spill an asset to disk in order to create a name out of it. Please take
+// care not to call it superfluously!
+func (ctx *conversionContext) makeTerraformInput(
+	name string,
+	old, v resource.PropertyValue,
+	tfs shim.Schema,
+	ps *SchemaInfo,
+	rawNames bool,
+) (interface{}, error) {
 	// For TypeList or TypeSet with MaxItems==1, we will have projected as a scalar
 	// nested value, and need to wrap it into a single-element array before passing to
 	// Terraform.
@@ -409,7 +417,7 @@ func (ctx *conversionContext) MakeTerraformInput(name string, old, v resource.Pr
 				oldElem = oldArr[i]
 			}
 			elemName := fmt.Sprintf("%v[%v]", name, i)
-			e, err := ctx.MakeTerraformInput(elemName, oldElem, elem, etfs, eps, rawNames)
+			e, err := ctx.makeTerraformInput(elemName, oldElem, elem, etfs, eps, rawNames)
 			if err != nil {
 				return nil, err
 			}
@@ -448,25 +456,46 @@ func (ctx *conversionContext) MakeTerraformInput(name string, old, v resource.Pr
 		}
 		return ps.Asset.TranslateArchive(v.ArchiveValue())
 	case v.IsObject():
-		var tfflds shim.SchemaMap
-		if tfs != nil {
-			if res, isres := tfs.Elem().(shim.Resource); isres {
-				tfflds = res.Schema()
-			}
-		}
-		var psflds map[string]*SchemaInfo
-		if ps != nil {
-			psflds = ps.Fields
-		}
 		var oldObject resource.PropertyMap
 		if old.IsObject() {
 			oldObject = old.ObjectValue()
 		}
 
-		input, err := ctx.MakeTerraformInputs(oldObject, v.ObjectValue(),
-			tfflds, psflds, rawNames || useRawNames(tfs))
-		if err != nil {
-			return nil, err
+		var input map[string]interface{}
+		if shimutil.IsOfTypeMap(tfs) {
+			var tfsElem shim.Schema
+			if tfs != nil {
+				if s, ok := tfs.Elem().(shim.Schema); ok {
+					tfsElem = s
+				}
+			}
+			var psElem *SchemaInfo
+			if ps != nil {
+				psElem = ps.Elem
+			}
+			var err error
+			input, err = ctx.makeMapTerraformInputs(oldObject, v.ObjectValue(),
+				tfsElem, psElem)
+			if err != nil {
+				return nil, err
+			}
+		} else {
+			var tfflds shim.SchemaMap
+			if tfs != nil {
+				if res, isres := tfs.Elem().(shim.Resource); isres {
+					tfflds = res.Schema()
+				}
+			}
+			var psflds map[string]*SchemaInfo
+			if ps != nil {
+				psflds = ps.Fields
+			}
+			var err error
+			input, err = ctx.makeObjectTerraformInputs(oldObject, v.ObjectValue(),
+				tfflds, psflds, rawNames)
+			if err != nil {
+				return nil, err
+			}
 		}
 
 		// If we have schema information that indicates that this value is being presented to a map-typed field whose
@@ -489,17 +518,70 @@ func (ctx *conversionContext) MakeTerraformInput(name string, old, v resource.Pr
 
 }
 
-// MakeTerraformInputs takes a property map plus custom schema info and does whatever is necessary
+// makeTerraformInputs takes a property map plus custom schema info and does whatever is necessary
 // to prepare it for use by Terraform.  Note that this function may have side effects, for instance
 // if it is necessary to spill an asset to disk in order to create a name out of it.  Please take
 // care not to call it superfluously!
-func (ctx *conversionContext) MakeTerraformInputs(
+func (ctx *conversionContext) makeTerraformInputs(
 	olds, news resource.PropertyMap,
 	tfs shim.SchemaMap,
 	ps map[string]*SchemaInfo,
-	rawNames bool,
 ) (map[string]interface{}, error) {
+	return ctx.makeObjectTerraformInputs(olds, news, tfs, ps, false /*rawNames*/)
+}
 
+// Should only be called from inside makeTerraformInputs. Variation for makeTerraformInputs used
+// when the schema indicates that the code is handling a map[string,X] case and not an object.
+func (ctx *conversionContext) makeMapTerraformInputs(
+	olds, news resource.PropertyMap,
+	tfsElement shim.Schema,
+	psElement *SchemaInfo,
+) (map[string]interface{}, error) {
+	result := make(map[string]interface{})
+
+	for key, value := range news {
+		// Map keys always preserved as-is, no translation necessary.
+		name := string(key)
+
+		var old resource.PropertyValue
+		if ctx.ApplyDefaults && olds != nil {
+			old = olds[key]
+		}
+
+		// If type information is lost (the map's element type is unknown), subsequent
+		// nested PropertyMap values will be treated by makeObjectTerraformInputs. In this
+		// case rawNames=true needs to make sure makeObjectTerraformInputs does not mangle
+		// pulumiStyleLabels into terraform_style_labels. This is the legacy behavior
+		// enforced by tests. It also makes intuitive sense: absent schema information the
+		// code should not be doing name mangling.
+		rawNames := tfsElement == nil
+
+		v, err := ctx.makeTerraformInput(name, old, value, tfsElement, psElement, rawNames)
+		if err != nil {
+			return nil, err
+		}
+		result[name] = v
+		glog.V(9).Infof("Created Terraform input: %v = %v", name, v)
+	}
+
+	if glog.V(5) {
+		for k, v := range result {
+			glog.V(5).Infof("Terraform input %v = %#v", k, v)
+		}
+	}
+
+	return result, nil
+}
+
+// Should only be called from inside makeTerraformInputs. This variation should only be handling the
+// case when an object type is expected, or else the schema is lost and the translation is not sure
+// of the expected type. The case when map types are expected is handled by makeMapTerraformInputs.
+func (ctx *conversionContext) makeObjectTerraformInputs(
+	olds, news resource.PropertyMap,
+	tfs shim.SchemaMap,
+	ps map[string]*SchemaInfo,
+	rawNames bool,
+) (map[string]interface{}, error) {
 	result := make(map[string]interface{})
 	tfAttributesToPulumiProperties := make(map[string]string)
 
@@ -544,7 +626,7 @@ func (ctx *conversionContext) MakeTerraformInputs(
 		}
 
 		// And then translate the property value.
-		v, err := ctx.MakeTerraformInput(name, old, value, tfi, psi, rawNames)
+		v, err := ctx.makeTerraformInput(name, old, value, tfi, psi, rawNames)
 		if err != nil {
 			return nil, err
 		}
@@ -674,7 +756,7 @@ func (ctx *conversionContext) applyDefaults(
 			key, tfi, psi := getInfoFromTerraformName(name, tfs, ps, rawNames)
 
 			if old, hasold := olds[key]; hasold && useOldDefault(key) {
-				v, err := ctx.MakeTerraformInput(name, resource.PropertyValue{},
+				v, err := ctx.makeTerraformInput(name, resource.PropertyValue{},
 					old, tfi, psi, rawNames)
 				if err != nil {
 					return err
@@ -717,7 +799,7 @@ func (ctx *conversionContext) applyDefaults(
 				defaultValue, source = v, "env vars"
 			} else if configKey := info.Default.Config; configKey != "" {
 				if v := ctx.ProviderConfig[resource.PropertyKey(configKey)]; !v.IsNull() {
-					tv, err := ctx.MakeTerraformInput(name, resource.PropertyValue{}, v, tfi, psi, rawNames)
+					tv, err := ctx.makeTerraformInput(name, resource.PropertyValue{}, v, tfi, psi, rawNames)
 					if err != nil {
 						return err
 					}
@@ -725,7 +807,7 @@ func (ctx *conversionContext) applyDefaults(
 				}
 			} else if info.Default.Value != nil {
 				v := resource.NewPropertyValue(info.Default.Value)
-				tv, err := ctx.MakeTerraformInput(name, resource.PropertyValue{}, v, tfi, psi, rawNames)
+				tv, err := ctx.makeTerraformInput(name, resource.PropertyValue{}, v, tfi, psi, rawNames)
 				if err != nil {
 					return err
 				}
@@ -827,7 +909,7 @@ func (ctx *conversionContext) applyDefaults(
 				key, tfi, psi := getInfoFromTerraformName(name, tfs, ps, rawNames)
 
 				if old, hasold := olds[key]; hasold && useOldDefault(key) {
-					v, err := ctx.MakeTerraformInput(name, resource.PropertyValue{}, old, tfi, psi, rawNames)
+					v, err := ctx.makeTerraformInput(name, resource.PropertyValue{}, old, tfi, psi, rawNames)
 					if err != nil {
 						valueErr = err
 						return false
@@ -1074,7 +1156,7 @@ func MakeTerraformOutput(p shim.Provider, v interface{},
 			if ps != nil {
 				psflds = ps.Fields
 			}
-			obj := MakeTerraformOutputs(p, outs, tfflds, psflds, assets, rawNames || useRawNames(tfs), supportsSecrets)
+			obj := MakeTerraformOutputs(p, outs, tfflds, psflds, assets, rawNames || shimutil.IsOfTypeMap(tfs), supportsSecrets)
 			return resource.NewObjectProperty(obj)
 		default:
 			contract.Failf("Unexpected TF output property value: %#v with type %#T", v, v)
@@ -1101,7 +1183,7 @@ func MakeTerraformConfig(ctx context.Context, p *Provider, m resource.PropertyMa
 		ProviderConfig: p.configValues,
 		Assets:         AssetTable{},
 	}
-	inputs, err := cctx.MakeTerraformInputs(nil, m, tfs, ps, false)
+	inputs, err := cctx.makeTerraformInputs(nil, m, tfs, ps)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -1176,7 +1258,7 @@ func MakeTerraformState(
 	// Mappable, but we use MapReplace because we use float64s and Terraform uses
 	// ints, to represent numbers.
 	cctx := &conversionContext{Ctx: ctx}
-	inputs, err := cctx.MakeTerraformInputs(nil, m, res.TF.Schema(), res.Schema.Fields, false)
+	inputs, err := cctx.makeTerraformInputs(nil, m, res.TF.Schema(), res.Schema.Fields)
 	if err != nil {
 		return nil, err
 	}
@@ -1219,16 +1301,6 @@ func IsMaxItemsOne(tfs shim.Schema, info *SchemaInfo) bool {
 	return tfs.MaxItems() == 1
 }
 
-// useRawNames returns true if raw, unmangled names should be preserved.  This is only true for Terraform maps with
-// an Elem that is not a shim.Resource.
-func useRawNames(tfs shim.Schema) bool {
-	if tfs == nil || tfs.Type() != shim.TypeMap {
-		return false
-	}
-	_, hasResourceElem := tfs.Elem().(shim.Resource)
-	return !hasResourceElem
-}
-
 // getInfoFromTerraformName does a map lookup to find the Pulumi name and schema info, if any.
 func getInfoFromTerraformName(key string,
 	tfs shim.SchemaMap, ps map[string]*SchemaInfo, rawName bool) (resource.PropertyKey,
@@ -1403,8 +1475,8 @@ func extractInputs(oldInput, newState resource.PropertyValue, tfs shim.Schema, p
 		for name, oldValue := range oldMap {
 			defaultElem := false
 			if newValue, ok := newMap[name]; ok {
-				_, etfs, eps := getInfoFromPulumiName(name, tfflds, psflds, rawNames || useRawNames(tfs))
-				oldMap[name], defaultElem = extractInputs(oldValue, newValue, etfs, eps, rawNames || useRawNames(tfs))
+				_, etfs, eps := getInfoFromPulumiName(name, tfflds, psflds, rawNames || shimutil.IsOfTypeMap(tfs))
+				oldMap[name], defaultElem = extractInputs(oldValue, newValue, etfs, eps, rawNames || shimutil.IsOfTypeMap(tfs))
 			} else {
 				delete(oldMap, name)
 			}
@@ -1511,7 +1583,7 @@ func extractSchemaInputs(state resource.PropertyValue, tfs shim.Schema, ps *Sche
 		a := state.ArrayValue()
 		v := make([]resource.PropertyValue, len(a))
 		for i := range a {
-			v[i] = extractSchemaInputs(a[i], etfs, eps, rawNames || useRawNames(tfs))
+			v[i] = extractSchemaInputs(a[i], etfs, eps, rawNames || shimutil.IsOfTypeMap(tfs))
 		}
 		return resource.NewArrayProperty(v)
 	case state.IsObject():
@@ -1530,13 +1602,13 @@ func extractSchemaInputs(state resource.PropertyValue, tfs shim.Schema, ps *Sche
 		obj := state.ObjectValue()
 		v := make(map[resource.PropertyKey]resource.PropertyValue, len(obj))
 		for k, e := range obj {
-			_, etfs, eps := getInfoFromPulumiName(k, tfflds, psflds, rawNames || useRawNames(tfs))
+			_, etfs, eps := getInfoFromPulumiName(k, tfflds, psflds, rawNames || shimutil.IsOfTypeMap(tfs))
 			if isInput := isMap || etfs != nil && (etfs.Optional() || etfs.Required()); !isInput {
 				glog.V(9).Infof("skipping '%v' (not an input)", k)
 				continue
 			}
 
-			ev := extractSchemaInputs(e, etfs, eps, rawNames || useRawNames(tfs))
+			ev := extractSchemaInputs(e, etfs, eps, rawNames || shimutil.IsOfTypeMap(tfs))
 			if mustSet := isMap || etfs != nil && (etfs.Required() || !isDefaultOrZeroValue(etfs, eps, ev)); !mustSet {
 				glog.V(9).Infof("skipping '%v' (not required + default or zero value)", k)
 				continue
diff --git a/pkg/tfgen/docs.go b/pkg/tfgen/docs.go
index b41c5d17..3860d261 100644
--- a/pkg/tfgen/docs.go
+++ b/pkg/tfgen/docs.go
@@ -1618,10 +1618,9 @@ func (s languages) Less(i, j int) bool {
 	} else if ii == notFound && jj != notFound {
 		// Only the second item is found, so it must come first:
 		return false
-	} else {
-		// Fall back to alphabetical if neither are found:
-		return s[i] < s[j]
 	}
+	// Fall back to alphabetical if neither are found:
+	return s[i] < s[j]
 }
 
 // hclMapToString takes a map of hclConversions to various languages and returns a single Markdown string for use in
diff --git a/pkg/tfgen/generate_schema.go b/pkg/tfgen/generate_schema.go
index 75fd980f..4879ed2c 100644
--- a/pkg/tfgen/generate_schema.go
+++ b/pkg/tfgen/generate_schema.go
@@ -67,17 +67,19 @@ type schemaNestedType struct {
 }
 
 type schemaNestedTypes struct {
-	nameToType map[string]*schemaNestedType
+	nameToType      map[string]*schemaNestedType
+	pathCorrections map[string]paths.TypePath
 }
 
-func gatherSchemaNestedTypesForModule(mod *module) map[string]*schemaNestedType {
+func gatherSchemaNestedTypesForModule(mod *module) (map[string]*schemaNestedType, map[string]paths.TypePath) {
 	nt := &schemaNestedTypes{
-		nameToType: make(map[string]*schemaNestedType),
+		nameToType:      make(map[string]*schemaNestedType),
+		pathCorrections: make(map[string]paths.TypePath),
 	}
 	for _, member := range mod.members {
 		nt.gatherFromMember(member)
 	}
-	return nt.nameToType
+	return nt.nameToType, nt.pathCorrections
 }
 
 func gatherSchemaNestedTypesForMember(member moduleMember) map[string]*schemaNestedType {
@@ -112,9 +114,7 @@ type declarer interface {
 	Name() string
 }
 
-func (nt *schemaNestedTypes) declareType(typePath paths.TypePath, declarer declarer, namePrefix, name string,
-	typ *propertyType, isInput bool) string {
-
+func (nt *schemaNestedTypes) typeName(namePrefix, name string, typ *propertyType) string {
 	// Generate a name for this nested type.
 	typeName := namePrefix + cases.Title(language.Und, cases.NoLower).String(name)
 
@@ -123,7 +123,21 @@ func (nt *schemaNestedTypes) declareType(typePath paths.TypePath, declarer decla
 		typeName = typ.nestedType.Name().String()
 	}
 
+	if typ.typ != "" && typ.kind == kindObject {
+		typeName = typ.typ.Name().String()
+	}
+
 	typ.name = typeName
+	return typ.name
+}
+
+// declareType emits the declared type into nt.
+//
+// declareType requires that:
+// 1. nt.typeName(..., typ) has been called previously on this type.
+// 2. All nested types have already been declare.
+func (nt *schemaNestedTypes) declareType(typePath paths.TypePath, declarer declarer,
+	typ *propertyType, isInput bool) {
 
 	required := codegen.StringSet{}
 	for _, p := range typ.properties {
@@ -140,8 +154,9 @@ func (nt *schemaNestedTypes) declareType(typePath paths.TypePath, declarer decla
 	}
 
 	// Merging makes sure that structurally identical types are shared and not generated more than once.
-	if existing, ok := nt.nameToType[typeName]; ok {
-		contract.Assertf(existing.declarer == declarer || existing.typ.equals(typ), "duplicate type %v", typeName)
+	if existing, ok := nt.nameToType[typ.name]; ok {
+		contract.Assertf(existing.declarer == declarer || existing.typ.equals(typ),
+			"%s declared twice with different values", typ.name)
 
 		// Remember that existing type is now also seen at the current typePath.
 		existing.typePaths.Add(typePath)
@@ -156,10 +171,10 @@ func (nt *schemaNestedTypes) declareType(typePath paths.TypePath, declarer decla
 		}
 
 		existing.typ, existing.required = typ, required
-		return typeName
+		return
 	}
 
-	nt.nameToType[typeName] = &schemaNestedType{
+	nt.nameToType[typ.name] = &schemaNestedType{
 		typ:             typ,
 		declarer:        declarer,
 		required:        required,
@@ -167,7 +182,6 @@ func (nt *schemaNestedTypes) declareType(typePath paths.TypePath, declarer decla
 		requiredOutputs: requiredOutputs,
 		typePaths:       paths.SingletonTypePathSet(typePath),
 	}
-	return typeName
 }
 
 func (nt *schemaNestedTypes) gatherFromProperties(pathContext paths.TypePath,
@@ -180,11 +194,23 @@ func (nt *schemaNestedTypes) gatherFromProperties(pathContext paths.TypePath,
 			name = inflector.Singularize(name)
 		}
 
-		nt.gatherFromPropertyType(paths.NewProperyPath(pathContext, p.propertyName),
-			declarer, namePrefix, name, p.typ, isInput)
+		var path paths.TypePath = paths.NewProperyPath(pathContext, p.propertyName)
+		if t := p.typ.typ; t != "" && p.typ.kind == kindObject {
+			namePrefix = ""
+			newPath := paths.NewRawPath(t, path)
+			nt.correctPath(path, newPath)
+			path = newPath
+		}
+
+		nt.gatherFromPropertyType(path, declarer, namePrefix, name, p.typ, isInput)
 	}
 }
 
+// Insert a type path redirect, setting src to point to dst.
+func (nt *schemaNestedTypes) correctPath(src, dst paths.TypePath) {
+	nt.pathCorrections[src.UniqueKey()] = dst
+}
+
 func (nt *schemaNestedTypes) gatherFromPropertyType(typePath paths.TypePath, declarer declarer, namePrefix,
 	name string, typ *propertyType, isInput bool) {
 
@@ -195,8 +221,19 @@ func (nt *schemaNestedTypes) gatherFromPropertyType(typePath paths.TypePath, dec
 				declarer, namePrefix, name, typ.element, isInput)
 		}
 	case kindObject:
-		baseName := nt.declareType(typePath, declarer, namePrefix, name, typ, isInput)
+		if typ.typ != "" {
+			namePrefix = ""
+			typePath = paths.NewRawPath(typ.typ, typePath)
+		}
+
+		// Because nt.declareType expects the declared type to be fully formed (to
+		// allow an equality comparison against other fully formed types), all
+		// nested types must themselves be fully formed.
+		//
+		// gatherFromProperties must be called before declareType.
+		baseName := nt.typeName(namePrefix, name, typ)
 		nt.gatherFromProperties(typePath, declarer, baseName, typ.properties, isInput)
+		nt.declareType(typePath, declarer, typ, isInput)
 	}
 }
 
@@ -247,13 +284,33 @@ func (g *schemaGenerator) genPackageSpec(pack *pkg) (pschema.PackageSpec, error)
 	spec.Attribution = fmt.Sprintf(attributionFormatString, g.info.Name, g.info.GetGitHubOrg(), g.info.GetGitHubHost())
 
 	var config []*variable
+	declaredTypes := map[string]*schemaNestedType{}
 	for _, mod := range pack.modules.values() {
 		// Generate nested types.
-		for _, t := range gatherSchemaNestedTypesForModule(mod) {
+		nestedTypes, pathCorrections := gatherSchemaNestedTypesForModule(mod)
+		if b := g.renamesBuilder; b != nil {
+			if b.pathCorrections == nil {
+				b.pathCorrections = pathCorrections
+			} else {
+				for k, v := range pathCorrections {
+					b.pathCorrections[k] = v
+				}
+			}
+		}
+		for _, t := range nestedTypes {
 			tok := g.genObjectTypeToken(t)
 			ts := g.genObjectType(t, false)
-			spec.Types[tok] = pschema.ComplexTypeSpec{
-				ObjectTypeSpec: ts,
+
+			if existing, ok := declaredTypes[tok]; ok {
+				if !t.typ.equals(existing.typ) {
+					return pschema.PackageSpec{},
+						fmt.Errorf("%s: conflicting type definitions", tok)
+				}
+			} else {
+				declaredTypes[tok] = t
+				spec.Types[tok] = pschema.ComplexTypeSpec{
+					ObjectTypeSpec: ts,
+				}
 			}
 		}
 
@@ -543,7 +600,10 @@ func (g *schemaGenerator) genProperty(prop *variable) pschema.PropertySpec {
 
 	language := map[string]pschema.RawMessage{}
 	if prop.info != nil && prop.info.CSharpName != "" {
-		language["csharp"] = rawMessage(map[string]string{"name": prop.info.CSharpName})
+		info := csgen.CSharpPropertyInfo{
+			Name: prop.info.CSharpName,
+		}
+		language["csharp"] = rawMessage(info)
 	}
 
 	var defaultValue interface{}
@@ -708,6 +768,14 @@ func (g *schemaGenerator) genResourceType(mod tokens.Module, res *resourceType)
 		})
 	}
 
+	spec.Language = map[string]pschema.RawMessage{}
+	if res.info != nil && res.info.CSharpName != "" {
+		info := csgen.CSharpResourceInfo{
+			Name: res.info.CSharpName,
+		}
+		spec.Language["csharp"] = rawMessage(info)
+	}
+
 	return spec
 }
 
@@ -764,11 +832,13 @@ func (g *schemaGenerator) genObjectTypeToken(typInfo *schemaNestedType) string {
 	}
 
 	mod := modulePlacementForTypeSet(g.pkg, typInfo.typePaths)
-	token := fmt.Sprintf("%s/%s:%s", mod.String(), name, name)
-
-	g.renamesBuilder.registerNamedObjectType(typInfo.typePaths, tokens.Type(token))
+	token := tokens.Type(fmt.Sprintf("%s/%s:%s", mod, name, name))
+	if typ.typ != "" {
+		token = typ.typ
+	}
 
-	return token
+	g.renamesBuilder.registerNamedObjectType(typInfo.typePaths, token)
+	return token.String()
 }
 
 func (g *schemaGenerator) genObjectType(typInfo *schemaNestedType, isTopLevel bool) pschema.ObjectTypeSpec {
@@ -905,7 +975,7 @@ func (g *schemaGenerator) schemaType(path paths.TypePath, typ *propertyType, out
 		return pschema.TypeSpec{Type: "object", AdditionalProperties: &additionalProperties}
 	case kindObject:
 		mod := modulePlacementForType(g.pkg, path)
-		ref := fmt.Sprintf("#/types/%s/%s:%s", mod.String(), typ.name, typ.name)
+		ref := fmt.Sprintf("#/types/%s/%s:%s", mod, typ.name, typ.name)
 		return pschema.TypeSpec{Ref: ref}
 	default:
 		contract.Failf("Unrecognized type kind: %v", typ.kind)
@@ -1128,6 +1198,8 @@ func modulePlacementForType(pkg tokens.Package, path paths.TypePath) tokens.Modu
 		return parentModuleOrSelf(m)
 	case *paths.ConfigPath:
 		return tokens.NewModuleToken(pkg, configMod)
+	case *paths.RawTypePath:
+		return pp.Raw().Module()
 	default:
 		contract.Assertf(false, "invalid ParentKind")
 		return ""
diff --git a/pkg/tfgen/internal/paths/paths.go b/pkg/tfgen/internal/paths/paths.go
index 4b2c0ae0..55d72539 100644
--- a/pkg/tfgen/internal/paths/paths.go
+++ b/pkg/tfgen/internal/paths/paths.go
@@ -336,3 +336,27 @@ func (s TypePathSet) Paths() []TypePath {
 	})
 	return res
 }
+
+// RawTypePath represents a type anchored from an opaque user provided type.
+type RawTypePath struct {
+	t              tokens.Type
+	structuralPath TypePath
+}
+
+func NewRawPath(typ tokens.Type, structuralPath TypePath) *RawTypePath {
+	return &RawTypePath{typ, structuralPath}
+}
+
+var _ TypePath = (*RawTypePath)(nil)
+
+func (p *RawTypePath) Parent() TypePath { return nil }
+
+// Useful for comparing paths.
+func (p *RawTypePath) UniqueKey() string { return p.t.String() }
+
+// Human friendly representation.
+func (p *RawTypePath) String() string { return p.t.Name().String() }
+
+func (p *RawTypePath) Raw() tokens.Type { return p.t }
+
+func (p *RawTypePath) StructuralPath() TypePath { return p.structuralPath }
diff --git a/pkg/tfgen/namecheck.go b/pkg/tfgen/namecheck.go
index 5368dc13..4b98fc4b 100644
--- a/pkg/tfgen/namecheck.go
+++ b/pkg/tfgen/namecheck.go
@@ -383,6 +383,8 @@ func locateTypByTypePath(prov tfbridge.ProviderInfo, path paths.TypePath) (typ,
 			return nil, err
 		}
 		return t.Element()
+	case *paths.RawTypePath:
+		return locateTypByTypePath(prov, p.StructuralPath())
 	default:
 		panic("impossible match in locateTypByTypePath")
 	}
diff --git a/pkg/tfgen/renames.go b/pkg/tfgen/renames.go
index 1fbffade..15e80dd0 100644
--- a/pkg/tfgen/renames.go
+++ b/pkg/tfgen/renames.go
@@ -70,6 +70,12 @@ type renamesBuilder struct {
 	functions       map[tokens.ModuleMember]string
 	objectTypes     map[string]tokens.Type
 	objectTypePaths map[tokens.Type]paths.TypePathSet
+
+	// pathCorrections accounts for the case where a user has explicitly specified a
+	// path, so it no longer matches it's expected location.
+	//
+	// This maps the unique key of a structural path to the user supplied path.
+	pathCorrections map[string]paths.TypePath
 }
 
 func newRenamesBuilder(pkg tokens.Package, resourcePrefix string) *renamesBuilder {
@@ -215,6 +221,8 @@ func (r *renamesBuilder) BuildRenames() (Renames, error) {
 }
 
 func (r renamesBuilder) findObjectTypeToken(path paths.TypePath) (tokens.Type, error) {
+	path = r.correctPath(path)
+
 	if p, ok := r.objectTypes[path.String()]; ok {
 		return p, nil
 	}
@@ -227,7 +235,20 @@ func (r renamesBuilder) findObjectTypeToken(path paths.TypePath) (tokens.Type, e
 	if p, ok := r.objectTypes[path.String()]; ok {
 		return p, nil
 	}
-	return "", fmt.Errorf("expected registerNamedObjectType to be called for %s", path.String())
+	return "", fmt.Errorf("expected registerNamedObjectType to be called for %s (%[1]T)", path)
+}
+
+func (r renamesBuilder) correctPath(path paths.TypePath) paths.TypePath {
+	if v, ok := r.pathCorrections[path.UniqueKey()]; ok {
+		return v
+	}
+
+	switch path := path.(type) {
+	case *paths.PropertyPath:
+		return paths.NewProperyPath(r.correctPath(path.Parent()), path.PropertyName)
+	default:
+		return path
+	}
 }
 
 func (r renamesBuilder) normalizeProviderStateToProviderInputs(p paths.TypePath) paths.TypePath {
@@ -237,9 +258,7 @@ func (r renamesBuilder) normalizeProviderStateToProviderInputs(p paths.TypePath)
 			return pp.ResourcePath.Inputs()
 		}
 		return p
-	case *paths.DataSourceMemberPath:
-		return p
-	case *paths.ConfigPath:
+	case *paths.DataSourceMemberPath, *paths.ConfigPath, *paths.RawTypePath:
 		return p
 	case *paths.ElementPath:
 		return paths.NewElementPath(r.normalizeProviderStateToProviderInputs(pp.Parent()))
diff --git a/pkg/tfshim/sdk-v2/cty.go b/pkg/tfshim/sdk-v2/cty.go
index 8ac28777..06382310 100644
--- a/pkg/tfshim/sdk-v2/cty.go
+++ b/pkg/tfshim/sdk-v2/cty.go
@@ -15,10 +15,20 @@
 package sdkv2
 
 import (
+	"fmt"
+	"math/big"
+
 	"github.com/golang/glog"
+
 	"github.com/hashicorp/go-cty/cty"
 	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
 	"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
+
+	"github.com/pulumi/pulumi/sdk/v3/go/common/util/contract"
+)
+
+const (
+	terraformUnknownVariableValue = "74D93920-ED26-11E3-AC10-0800200C9A66"
 )
 
 // makeResourceRawConfig converts the decoded Go values in a terraform.ResourceConfig into a cty.Value that is
@@ -32,10 +42,7 @@ func makeResourceRawConfig(
 		return makeResourceRawConfigClassic(config, resource)
 	}
 
-	// The method JSONMapToStateValue has State in its name but the implementation is generic, and fits the use case
-	// here. What it does is simply parsing raw map[string]interface{} data into a Object value with a schema that
-	// corresponds to the Resource schema.
-	value, err := schema.JSONMapToStateValue(config.Raw, resource.CoreConfigSchema())
+	value, err := recoverAndCoerceCtyValue(resource, config.Raw)
 	if err == nil {
 		return value
 	}
@@ -61,3 +68,183 @@ func makeResourceRawConfigClassic(config *terraform.ResourceConfig, resource *sc
 	}
 	return coerced
 }
+
+// This method started off calling [schema.JSONMapToStateValue], but it turns out that it is not
+// suitable for processing unknown values encoded as [terraformUnknownVariableValue]. The latest
+// implementation retains CoerceValue call from the JSONMapToStateValue, but does an explicit
+// type-driven recursive recovery pass from any to cty.Value first, instead of doing this
+// transformation via an intermediate JSON form. The pass is made aware of
+// terraformUnknownVariableValue.
+//
+// One possibility to refactor this code is to have MakeTerraformInput return cty.Value directly
+// instead of implicit any encoding.
+func recoverAndCoerceCtyValue(resource *schema.Resource, value any) (cty.Value, error) {
+	c, err := recoverCtyValue(resource.CoreConfigSchema().ImpliedType(), value)
+	if err != nil {
+		return cty.NilVal, fmt.Errorf("recoverCtyValue failed: %w", err)
+	}
+	cv, err := resource.CoreConfigSchema().CoerceValue(c)
+	if err != nil {
+		return cty.NilVal, fmt.Errorf("CoerceValue failed: %w", err)
+	}
+	return cv, nil
+}
+
+// See [recoverAndCoerceCtyValue] as the main use case. Takes a value produced by
+// tfbridge.MakeTerraformInput and converts it to a cty.Value of the desired type dT. This recursive
+// method tracks desired type dT through containers such as objects, maps and lists. For scalars, it
+// recognizes unknowns encoded as terraformUnknownVariableValue. Missing values for object maps are
+// populated with nulls (important to avoid panics in TF internal code), and extraneous values
+// discarded. No type conversions such as string to bool are attempted, as the code calls
+// CoerceValue on the result of this method and that traversal takes care of that.
+func recoverCtyValue(dT cty.Type, value interface{}) (cty.Value, error) {
+	switch value := value.(type) {
+	case nil:
+		return cty.NullVal(dT), nil
+	case map[string]interface{}:
+		switch {
+		case dT.IsMapType():
+			return recoverCtyValueOfMapType(dT, value)
+		case dT.IsObjectType():
+			return recoverCtyValueOfObjectType(dT, value)
+		default:
+			return cty.NilVal, fmt.Errorf("Cannot reconcile map %v to %v", value, dT)
+		}
+	case []interface{}:
+		switch {
+		case dT.IsTupleType():
+			return recoverCtyValueOfTupleType(dT, value)
+		case dT.IsSetType():
+			return recoverCtyValueOfSetType(dT, value)
+		case dT.IsListType():
+			return recoverCtyValueOfListType(dT, value)
+		default:
+			return cty.NilVal, fmt.Errorf("Cannot reconcile slice %v to %v", value, dT)
+		}
+	case string:
+		if value == terraformUnknownVariableValue {
+			return cty.UnknownVal(dT), nil
+		}
+		return cty.StringVal(value), nil
+	case bool:
+		return cty.BoolVal(value), nil
+	case int:
+		return cty.NumberIntVal(int64(value)), nil
+	case int64:
+		return cty.NumberIntVal(value), nil
+	case uint64:
+		return cty.NumberUIntVal(value), nil
+	case float64:
+		return cty.NumberFloatVal(value), nil
+	case *big.Float:
+		return cty.NumberVal(value), nil
+	case uint8:
+		return cty.NumberIntVal(int64(value)), nil
+	case uint16:
+		return cty.NumberIntVal(int64(value)), nil
+	case uint32:
+		return cty.NumberIntVal(int64(value)), nil
+	case int8:
+		return cty.NumberIntVal(int64(value)), nil
+	case int16:
+		return cty.NumberIntVal(int64(value)), nil
+	case int32:
+		return cty.NumberIntVal(int64(value)), nil
+	case float32:
+		return cty.NumberFloatVal(float64(value)), nil
+	default:
+		return cty.NilVal, fmt.Errorf("Cannot recover cty.Value\n"+
+			"  Desired type: %v\n"+
+			"  Value: %v\n"+
+			"  Actual type: %#T\n",
+			dT.GoString(), value, value)
+	}
+}
+
+func recoverCtyValueOfMapType(dT cty.Type, value map[string]interface{}) (cty.Value, error) {
+	contract.Assertf(dT.IsMapType(), "recoverCtyValueOfMapType expected a Map, got %v", dT)
+	eT := dT.ElementType()
+	attrs := map[string]cty.Value{}
+	for k, v := range value {
+		var err error
+		attrs[k], err = recoverCtyValue(eT, v)
+		if err != nil {
+			return cty.NilVal, err
+		}
+	}
+	if len(attrs) == 0 {
+		return cty.MapValEmpty(eT), nil
+	}
+	return cty.MapVal(attrs), nil
+}
+
+func recoverCtyValueOfObjectType(dT cty.Type, value map[string]interface{}) (cty.Value, error) {
+	contract.Assertf(dT.IsObjectType(), "recoverCtyValueOfObjectType expected an Object, got %v", dT)
+	aT := dT.AttributeTypes()
+	if len(aT) == 0 {
+		return cty.EmptyObjectVal, nil
+	}
+	attrs := map[string]cty.Value{}
+	for attrName, attrType := range aT {
+		if v, ok := value[attrName]; ok {
+			var err error
+			attrs[attrName], err = recoverCtyValue(attrType, v)
+			if err != nil {
+				return cty.NilVal, err
+			}
+		} else {
+			attrs[attrName] = cty.NullVal(attrType)
+		}
+	}
+	return cty.ObjectVal(attrs), nil
+}
+
+func recoverCtyValueOfListType(dT cty.Type, values []interface{}) (cty.Value, error) {
+	contract.Assertf(dT.IsListType(), "recoverCtyValueOfListType expected a List, got %v", dT)
+	eT := dT.ElementType()
+	vals := []cty.Value{}
+	for _, v := range values {
+		rv, err := recoverCtyValue(eT, v)
+		if err != nil {
+			return cty.NilVal, err
+		}
+		vals = append(vals, rv)
+	}
+	if len(vals) == 0 {
+		return cty.ListValEmpty(eT), nil
+	}
+	return cty.ListVal(vals), nil
+}
+
+func recoverCtyValueOfSetType(dT cty.Type, values []interface{}) (cty.Value, error) {
+	contract.Assertf(dT.IsSetType(), "recoverCtyValueOfSetType expected a Set, got %v", dT)
+	eT := dT.ElementType()
+	vals := []cty.Value{}
+	for _, v := range values {
+		rv, err := recoverCtyValue(eT, v)
+		if err != nil {
+			return cty.NilVal, err
+		}
+		vals = append(vals, rv)
+	}
+	if len(vals) == 0 {
+		return cty.SetValEmpty(eT), nil
+	}
+	return cty.SetVal(vals), nil
+}
+
+func recoverCtyValueOfTupleType(dT cty.Type, values []interface{}) (cty.Value, error) {
+	contract.Assertf(dT.IsTupleType(), "recoverCtyValueOfTulpeType expected a Tuple, got %v", dT)
+	vals := []cty.Value{}
+	for i, v := range values {
+		rv, err := recoverCtyValue(dT.TupleElementType(i), v)
+		if err != nil {
+			return cty.NilVal, err
+		}
+		vals = append(vals, rv)
+	}
+	if len(vals) == 0 {
+		return cty.EmptyTupleVal, nil
+	}
+	return cty.TupleVal(vals), nil
+}
diff --git a/pkg/tfshim/sdk-v2/provider.go b/pkg/tfshim/sdk-v2/provider.go
index 64735184..f90b86f0 100644
--- a/pkg/tfshim/sdk-v2/provider.go
+++ b/pkg/tfshim/sdk-v2/provider.go
@@ -53,8 +53,6 @@ type v2Provider struct {
 	opts []providerOption
 }
 
-var _ shim.ProviderWithContext = (*v2Provider)(nil)
-
 func NewProvider(p *schema.Provider, opts ...providerOption) shim.Provider {
 	return v2Provider{
 		tf:   p,
@@ -87,27 +85,10 @@ func (p v2Provider) ValidateDataSource(t string, c shim.ResourceConfig) ([]strin
 }
 
 func (p v2Provider) Configure(c shim.ResourceConfig) error {
-	return errors(p.tf.Configure(context.Background(), configFromShim(c)))
-}
-
-func (p v2Provider) ConfigureWithContext(ctx context.Context, c shim.ResourceConfig) error {
-	return errors(p.tf.Configure(ctx, configFromShim(c)))
+	return errors(p.tf.Configure(context.TODO(), configFromShim(c)))
 }
 
-func (p v2Provider) Apply(
-	t string,
-	s shim.InstanceState,
-	d shim.InstanceDiff,
-) (shim.InstanceState, error) {
-	return p.ApplyWithContext(context.Background(), t, s, d)
-}
-
-func (p v2Provider) ApplyWithContext(
-	ctx context.Context,
-	t string,
-	s shim.InstanceState,
-	d shim.InstanceDiff,
-) (shim.InstanceState, error) {
+func (p v2Provider) Apply(t string, s shim.InstanceState, d shim.InstanceDiff) (shim.InstanceState, error) {
 	r, ok := p.tf.ResourcesMap[t]
 	if !ok {
 		return nil, fmt.Errorf("unknown resource %v", t)
@@ -116,24 +97,11 @@ func (p v2Provider) ApplyWithContext(
 	if err != nil {
 		return nil, fmt.Errorf("failed to upgrade resource state: %w", err)
 	}
-	state, diags := r.Apply(ctx, state, diffFromShim(d), p.tf.Meta())
+	state, diags := r.Apply(context.TODO(), state, diffFromShim(d), p.tf.Meta())
 	return stateToShim(r, state), errors(diags)
 }
 
-func (p v2Provider) Refresh(
-	t string,
-	s shim.InstanceState,
-	c shim.ResourceConfig,
-) (shim.InstanceState, error) {
-	return p.RefreshWithContext(context.Background(), t, s, c)
-}
-
-func (p v2Provider) RefreshWithContext(
-	ctx context.Context,
-	t string,
-	s shim.InstanceState,
-	c shim.ResourceConfig,
-) (shim.InstanceState, error) {
+func (p v2Provider) Refresh(t string, s shim.InstanceState, c shim.ResourceConfig) (shim.InstanceState, error) {
 	opts, err := getProviderOptions(p.opts)
 	if err != nil {
 		return nil, err
@@ -157,43 +125,21 @@ func (p v2Provider) RefreshWithContext(
 	return stateToShim(r, state), errors(diags)
 }
 
-func (p v2Provider) ReadDataDiff(
-	t string,
-	c shim.ResourceConfig,
-) (shim.InstanceDiff, error) {
-	return p.ReadDataDiffWithContext(context.Background(), t, c)
-}
-
-func (p v2Provider) ReadDataDiffWithContext(
-	ctx context.Context,
-	t string,
-	c shim.ResourceConfig,
-) (shim.InstanceDiff, error) {
+func (p v2Provider) ReadDataDiff(t string, c shim.ResourceConfig) (shim.InstanceDiff, error) {
 	r, ok := p.tf.DataSourcesMap[t]
 	if !ok {
 		return nil, fmt.Errorf("unknown resource %v", t)
 	}
-	diff, err := r.Diff(ctx, nil, configFromShim(c), p.tf.Meta())
+	diff, err := r.Diff(context.TODO(), nil, configFromShim(c), p.tf.Meta())
 	return diffToShim(diff), err
 }
 
-func (p v2Provider) ReadDataApply(
-	t string,
-	d shim.InstanceDiff,
-) (shim.InstanceState, error) {
-	return p.ReadDataApplyWithContext(context.Background(), t, d)
-}
-
-func (p v2Provider) ReadDataApplyWithContext(
-	ctx context.Context,
-	t string,
-	d shim.InstanceDiff,
-) (shim.InstanceState, error) {
+func (p v2Provider) ReadDataApply(t string, d shim.InstanceDiff) (shim.InstanceState, error) {
 	r, ok := p.tf.DataSourcesMap[t]
 	if !ok {
 		return nil, fmt.Errorf("unknown resource %v", t)
 	}
-	state, diags := r.ReadDataApply(ctx, diffFromShim(d), p.tf.Meta())
+	state, diags := r.ReadDataApply(context.TODO(), diffFromShim(d), p.tf.Meta())
 	return stateToShim(r, state), errors(diags)
 }
 
diff --git a/pkg/tfshim/sdk-v2/provider_diff.go b/pkg/tfshim/sdk-v2/provider_diff.go
index 08aad150..55099c13 100644
--- a/pkg/tfshim/sdk-v2/provider_diff.go
+++ b/pkg/tfshim/sdk-v2/provider_diff.go
@@ -26,20 +26,7 @@ import (
 	shim "github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tfshim"
 )
 
-func (p v2Provider) Diff(
-	t string,
-	s shim.InstanceState,
-	c shim.ResourceConfig,
-) (shim.InstanceDiff, error) {
-	return p.DiffWithContext(context.Background(), t, s, c)
-}
-
-func (p v2Provider) DiffWithContext(
-	ctx context.Context,
-	t string,
-	s shim.InstanceState,
-	c shim.ResourceConfig,
-) (shim.InstanceDiff, error) {
+func (p v2Provider) Diff(t string, s shim.InstanceState, c shim.ResourceConfig) (shim.InstanceDiff, error) {
 	if c == nil {
 		return diffToShim(&terraform.InstanceDiff{Destroy: true}), nil
 	}
@@ -73,7 +60,7 @@ func (p v2Provider) DiffWithContext(
 		}
 	}
 
-	diff, err := p.simpleDiff(ctx, opts.diffStrategy, r, state, config, rawConfig, p.tf.Meta())
+	diff, err := p.simpleDiff(opts.diffStrategy, r, state, config, rawConfig, p.tf.Meta())
 	if diff != nil {
 		diff.RawConfig = rawConfig
 	}
@@ -81,7 +68,6 @@ func (p v2Provider) DiffWithContext(
 }
 
 func (p v2Provider) simpleDiff(
-	ctx context.Context,
 	diffStrat DiffStrategy,
 	res *schema.Resource,
 	s *terraform.InstanceState,
@@ -89,6 +75,7 @@ func (p v2Provider) simpleDiff(
 	rawConfigVal hcty.Value,
 	meta interface{},
 ) (*terraform.InstanceDiff, error) {
+	ctx := context.TODO()
 
 	switch diffStrat {
 	case ClassicDiff:
diff --git a/pkg/tfshim/shim.go b/pkg/tfshim/shim.go
index 7198c3e3..1663b0ca 100644
--- a/pkg/tfshim/shim.go
+++ b/pkg/tfshim/shim.go
@@ -1,7 +1,6 @@
 package shim
 
 import (
-	"context"
 	"time"
 )
 
@@ -208,73 +207,3 @@ type Provider interface {
 	NewResourceConfig(object map[string]interface{}) ResourceConfig
 	IsSet(v interface{}) ([]interface{}, bool)
 }
-
-type ProviderWithContext interface {
-	Provider
-
-	ConfigureWithContext(ctx context.Context, c ResourceConfig) error
-	DiffWithContext(ctx context.Context, t string, s InstanceState, c ResourceConfig) (InstanceDiff, error)
-	ApplyWithContext(ctx context.Context, t string, s InstanceState, d InstanceDiff) (InstanceState, error)
-	RefreshWithContext(ctx context.Context, t string, s InstanceState, c ResourceConfig) (InstanceState, error)
-
-	ReadDataDiffWithContext(ctx context.Context, t string, c ResourceConfig) (InstanceDiff, error)
-	ReadDataApplyWithContext(ctx context.Context, t string, d InstanceDiff) (InstanceState, error)
-}
-
-func NewProviderWithContext(p Provider) ProviderWithContext {
-	if pwc, ok := p.(ProviderWithContext); ok {
-		return pwc
-	}
-	return &providerWithIgnoredContext{p}
-}
-
-type providerWithIgnoredContext struct {
-	Provider
-}
-
-func (p *providerWithIgnoredContext) ConfigureWithContext(_ context.Context, c ResourceConfig) error {
-	return p.Provider.Configure(c)
-}
-
-func (p *providerWithIgnoredContext) DiffWithContext(
-	_ context.Context,
-	t string,
-	s InstanceState,
-	c ResourceConfig,
-) (InstanceDiff, error) {
-	return p.Provider.Diff(t, s, c)
-}
-
-func (p *providerWithIgnoredContext) ApplyWithContext(
-	_ context.Context,
-	t string,
-	s InstanceState,
-	d InstanceDiff,
-) (InstanceState, error) {
-	return p.Provider.Apply(t, s, d)
-}
-
-func (p *providerWithIgnoredContext) RefreshWithContext(
-	_ context.Context,
-	t string,
-	s InstanceState,
-	c ResourceConfig,
-) (InstanceState, error) {
-	return p.Provider.Refresh(t, s, c)
-}
-
-func (p *providerWithIgnoredContext) ReadDataDiffWithContext(
-	_ context.Context,
-	t string,
-	c ResourceConfig,
-) (InstanceDiff, error) {
-	return p.Provider.ReadDataDiff(t, c)
-}
-
-func (p *providerWithIgnoredContext) ReadDataApplyWithContext(
-	ctx context.Context,
-	t string,
-	d InstanceDiff,
-) (InstanceState, error) {
-	return p.Provider.ReadDataApply(t, d)
-}
diff --git a/pkg/tfshim/util/types.go b/pkg/tfshim/util/types.go
new file mode 100644
index 00000000..25a9793a
--- /dev/null
+++ b/pkg/tfshim/util/types.go
@@ -0,0 +1,30 @@
+// Copyright 2016-2023, Pulumi Corporation.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package util
+
+import (
+	"github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tfshim"
+)
+
+// isOfTypeMap detects schemas indicating a map[string,X] type. Due to a quirky encoding of
+// single-nested Terraform blocks, it is insufficient to just check for tfs.Type() == shim.TypeMap.
+// See [shim.Schema.Elem()] comment for all the details of the encoding.
+func IsOfTypeMap(tfs shim.Schema) bool {
+	if tfs == nil || tfs.Type() != shim.TypeMap {
+		return false
+	}
+	_, hasResourceElem := tfs.Elem().(shim.Resource)
+	return !hasResourceElem
+}
diff --git a/unstable/logging/envvars.go b/unstable/logging/envvars.go
deleted file mode 100644
index 3068f796..00000000
--- a/unstable/logging/envvars.go
+++ /dev/null
@@ -1,12 +0,0 @@
-package logging
-
-const (
-	// Log verbosity is controlled by the TF_LOG environment variable (set to TRACE, DEBUG, INFO, WARN,
-	// ERROR or OFF). By default, INFO-level logs are emitted.
-	//
-	// See also:
-	//
-	// - https://developer.hashicorp.com/terraform/plugin/log/writing
-	// - https://www.pulumi.com/docs/support/troubleshooting
-	tfLogEnvVar = "TF_LOG"
-)
diff --git a/unstable/logging/logging.go b/unstable/logging/logging.go
index 54743054..5e964dbf 100644
--- a/unstable/logging/logging.go
+++ b/unstable/logging/logging.go
@@ -15,91 +15,18 @@
 package logging
 
 import (
-	"context"
 	"fmt"
-	"io"
-	"os"
-	"regexp"
-	"strings"
+)
 
-	"github.com/golang/glog"
-	"github.com/hashicorp/go-hclog"
-	"github.com/hashicorp/terraform-plugin-log/tflog"
-	"github.com/hashicorp/terraform-plugin-log/tfsdklog"
+import (
+	"context"
 
+	"github.com/golang/glog"
 	"github.com/pulumi/pulumi/pkg/v3/resource/provider"
 	"github.com/pulumi/pulumi/sdk/v3/go/common/diag"
 	"github.com/pulumi/pulumi/sdk/v3/go/common/resource"
 )
 
-// See InitLogging.
-type LogOptions struct {
-	LogSink         Sink
-	ProviderName    string
-	ProviderVersion string
-	URN             resource.URN
-}
-
-// Sets up Context-scoped loggers to route Terraform logs to the Pulumi CLI process so they are
-// visible to the user.
-//
-// Log verbosity is controlled by the TF_LOG environment variable (set to TRACE, DEBUG, INFO, WARN,
-// ERROR or OFF). By default, INFO-level logs are emitted.
-//
-// See also:
-//
-// - https://developer.hashicorp.com/terraform/plugin/log/writing
-// - https://www.pulumi.com/docs/support/troubleshooting
-func InitLogging(ctx context.Context, opts LogOptions) context.Context {
-	ctx = setupRootLoggers(ctx, newLogSinkWriter(ctx, opts.LogSink))
-
-	if opts.URN != "" {
-		ctx = tflog.SetField(ctx, "urn", string(opts.URN))
-		ctx = tfsdklog.SetField(ctx, "urn", string(opts.URN))
-	}
-
-	if opts.ProviderName != "" {
-		p := opts.ProviderName
-		if opts.ProviderVersion != "" {
-			p += "@" + opts.ProviderVersion
-		}
-		ctx = tflog.SetField(ctx, "provider", p)
-		ctx = tfsdklog.SetField(ctx, "provider", p)
-	}
-
-	// This call needs to happen after urn and provider fields are set, otherwise logs emitted
-	// by SDKv2 code against subsystems are not tagged with the urn and provider fields.
-	ctx = setupSubsystems(ctx, opts)
-
-	return context.WithValue(ctx, CtxKey, newHost[logLike](ctx, opts.LogSink, opts.URN, func(l *host[logLike]) logLike {
-		return l
-	}))
-}
-
-// Providers based on https://developer.hashicorp.com/terraform/plugin/sdkv2 emit logs via
-// helper_schema and helper_resource sub-systems, that need to be registered here.
-func setupSubsystems(ctx context.Context, opts LogOptions) context.Context {
-
-	// TF providers respect finer-grained control via TF_LOG_SDK and
-	// TF_LOG_SDK_HELPER_RESOURCE variables, but only TF_LOG is respected here for the
-	// moment, as that is the only option documented for Pulumi.
-	level := tfsdklog.WithLevelFromEnv(tfLogEnvVar)
-
-	ctx = tfsdklog.NewSubsystem(ctx, "helper_schema",
-		tfsdklog.WithAdditionalLocationOffset(1),
-		level,
-		tfsdklog.WithRootFields(), // ensure urn and provider field tagging
-	)
-
-	ctx = tfsdklog.NewSubsystem(ctx, "helper_resource",
-		tfsdklog.WithAdditionalLocationOffset(1),
-		level,
-		tfsdklog.WithRootFields(), // ensure urn and provider field tagging
-	)
-
-	return ctx
-}
-
 type ctxKey struct{}
 
 // The key used to retrieve tfbridge.Logger from a context.
@@ -122,7 +49,7 @@ var _ Sink = (*provider.HostClient)(nil)
 // unable to name either type here, we need to parameterize [Host] with `L`, where `L` is
 // always [tfbridge.Log]. This allows us to return `L` from Host[tfbridge.Log].Status()
 // and satisfy the [tfbridge.Logger] interface.
-type host[L any] struct {
+type Host[L any] struct {
 	ctx  context.Context
 	sink Sink
 	urn  resource.URN
@@ -132,23 +59,23 @@ type host[L any] struct {
 
 	// This is the identity function, but must be defined at the call-site because we
 	// cannot name [tfbridge.Log].
-	mkStatus func(*host[L]) L
+	mkStatus func(*Host[L]) L
 }
 
-func newHost[L any](ctx context.Context,
+func NewHost[L any](ctx context.Context,
 	sink Sink,
 	urn resource.URN,
-	mkStatus func(*host[L]) L,
-) *host[L] {
+	mkStatus func(*Host[L]) L,
+) *Host[L] {
 	// This nil check catches half-nil fat pointers from casting
 	// `*provider.HostClient` to `Sink`.
 	if host, ok := sink.(*provider.HostClient); ok && host == nil {
 		sink = nil
 	}
-	return &host[L]{ctx, sink, urn, false /*status*/, mkStatus}
+	return &Host[L]{ctx, sink, urn, false /*status*/, mkStatus}
 }
 
-func (l *host[L]) f(severity diag.Severity, msg string) {
+func (l *Host[L]) f(severity diag.Severity, msg string) {
 	if l.sink != nil {
 		f := l.sink.Log
 		if l.status {
@@ -179,177 +106,13 @@ func (l *host[L]) f(severity diag.Severity, msg string) {
 	glog.V(9).Infof("[%s]: %q", sev, msg)
 }
 
-func (l *host[L]) Status() L {
+func (l *Host[L]) Status() L {
 	copy := *l
 	copy.status = true
 	return l.mkStatus(&copy)
 }
 
-func (l *host[L]) StatusUntyped() any {
-	return l.Status()
-}
-
-func (l *host[L]) Debug(msg string) { l.f(diag.Debug, msg) }
-func (l *host[L]) Info(msg string)  { l.f(diag.Info, msg) }
-func (l *host[L]) Warn(msg string)  { l.f(diag.Warning, msg) }
-func (l *host[L]) Error(msg string) { l.f(diag.Error, msg) }
-
-// Directs any logs written using the tflog API in the given Context to the given output.
-func setupRootLoggers(ctx context.Context, output io.Writer) context.Context {
-	desiredLevel := parseTfLogEnvVar()
-	sdkLoggerOptions := makeLoggerOptions("sdk", desiredLevel, output)
-	ctx = context.WithValue(ctx, sdkKey, hclog.New(sdkLoggerOptions))
-	ctx = context.WithValue(ctx, sdkOptionsKey, sdkLoggerOptions)
-	providerLoggerOptions := makeLoggerOptions("provider", desiredLevel, output)
-	ctx = context.WithValue(ctx, providerKey, hclog.New(providerLoggerOptions))
-	ctx = context.WithValue(ctx, providerOptionsKey, providerLoggerOptions)
-	return ctx
-}
-
-// Choose the default level carefully: logs at this level or higher (more severe) will be shown to the user of Pulumi
-// CLI directly by default. Experimentally it seems that Info is too verbose, for example Cloudflare provider emits
-// routine authentication messages at INFO level.
-func defaultTFLogLevel() hclog.Level {
-	return hclog.Warn
-}
-
-func makeLoggerOptions(name string, level hclog.Level, output io.Writer) *hclog.LoggerOptions {
-	if level == hclog.NoLevel {
-		level = defaultTFLogLevel()
-	}
-	return &hclog.LoggerOptions{
-		Name:              name,
-		Output:            output,
-		Level:             level,
-		IndependentLevels: true,
-		IncludeLocation:   true,
-		TimeFormat:        " ", // Do not print time
-
-		// Empirically the value of 1 seems to work in the current Pulumi setup, @caller field now points to the
-		// file where the logging originates.
-		AdditionalLocationOffset: 1,
-	}
-}
-
-// Re-interprets structured JSON logs as calls against LogSink. To be used with SetupRootLoggers.
-func newLogSinkWriter(ctx context.Context, sink Sink) io.Writer {
-	desiredLevel := parseTfLogEnvVar()
-	return &logSinkWriter{
-		desiredLevel: desiredLevel,
-		ctx:          ctx,
-		sink:         sink,
-	}
-}
-
-type logSinkWriter struct {
-	desiredLevel hclog.Level
-	ctx          context.Context
-	sink         Sink
-}
-
-var _ io.Writer = &logSinkWriter{}
-
-func (w *logSinkWriter) Write(p []byte) (n int, err error) {
-	n = len(p)
-	if w.sink == nil {
-		return
-	}
-
-	raw := string(p)
-	level, raw := parseLevelFromRawString(raw)
-
-	if level < w.desiredLevel {
-		return
-	}
-
-	urn, raw := parseUrnFromRawString(raw)
-	severity := logLevelToSeverity(level)
-
-	err = w.sink.Log(w.ctx, severity, urn, raw)
-	return
-}
-
-func parseTfLogEnvVar() hclog.Level {
-	env, present := os.LookupEnv(tfLogEnvVar)
-	if !present {
-		return hclog.NoLevel
-	}
-	switch strings.ToUpper(env) {
-	case "ERROR":
-		return hclog.Error
-	case "WARN":
-		return hclog.Warn
-	case "INFO":
-		return hclog.Info
-	case "TRACE":
-		return hclog.Trace
-	case "DEBUG":
-		return hclog.Debug
-	case "OFF":
-		return hclog.Off
-	default:
-		return hclog.NoLevel
-	}
-}
-
-var quotedUrnPattern = regexp.MustCompile(`[ ]urn=["]([^"]+)["]`)
-var bareUrnPattern = regexp.MustCompile(`[ ]urn=([^ ]+)`)
-
-func parseUrnFromRawString(s string) (resource.URN, string) {
-	if ok := quotedUrnPattern.FindStringSubmatch(s); len(ok) > 0 {
-		return resource.URN(ok[1]), strings.Replace(s, ok[0], "", 1)
-	}
-	if ok := bareUrnPattern.FindStringSubmatch(s); len(ok) > 0 {
-		return resource.URN(ok[1]), strings.Replace(s, ok[0], "", 1)
-	}
-	return "", s
-}
-
-func parseLevelFromRawString(s string) (hclog.Level, string) {
-	i := strings.Index(s, "[")
-	j := strings.Index(s[i:], "]")
-	remainder := s[0:i] + s[i+j+2:]
-	switch s[i : i+j+1] {
-	case "[ERROR]":
-		return hclog.Error, remainder
-	case "[WARN]":
-		return hclog.Warn, remainder
-	case "[DEBUG]":
-		return hclog.Debug, remainder
-	case "[INFO]":
-		return hclog.Info, remainder
-	case "[TRACE]":
-		return hclog.Trace, remainder
-	default:
-		return hclog.Trace, remainder
-	}
-}
-
-func logLevelToSeverity(l hclog.Level) diag.Severity {
-	switch l {
-	case hclog.Error:
-		return diag.Error
-	case hclog.Warn:
-		return diag.Warning
-	case hclog.Info:
-		return diag.Info
-	case hclog.Debug:
-		return diag.Debug
-	case hclog.Trace:
-		return diag.Debug
-	default:
-		return diag.Debug
-	}
-}
-
-func getLogger(ctx context.Context) *host[logLike] {
-	return ctx.Value(CtxKey).(*host[logLike])
-}
-
-// The set of logs available to show to the user
-type logLike interface {
-	Debug(msg string)
-	Info(msg string)
-	Warn(msg string)
-	Error(msg string)
-}
+func (l *Host[L]) Debug(msg string) { l.f(diag.Debug, msg) }
+func (l *Host[L]) Info(msg string)  { l.f(diag.Info, msg) }
+func (l *Host[L]) Warn(msg string)  { l.f(diag.Warning, msg) }
+func (l *Host[L]) Error(msg string) { l.f(diag.Error, msg) }
diff --git a/x/muxer/go.mod b/x/muxer/go.mod
index e2c976e1..7753994a 100644
--- a/x/muxer/go.mod
+++ b/x/muxer/go.mod
@@ -20,17 +20,19 @@ require (
 	github.com/cyphar/filepath-securejoin v0.2.4 // indirect
 	github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
 	github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
-	github.com/mattn/go-isatty v0.0.18 // indirect
+	github.com/mattn/go-isatty v0.0.19 // indirect
 	github.com/mattn/go-localereader v0.0.1 // indirect
-	github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b // indirect
+	github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
 	github.com/muesli/cancelreader v0.2.2 // indirect
 	github.com/muesli/reflow v0.3.0 // indirect
-	github.com/muesli/termenv v0.15.1 // indirect
-	github.com/pulumi/esc v0.5.7-0.20231030195049-f71961c0d5fa // indirect
-	golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect
-	golang.org/x/mod v0.13.0 // indirect
-	golang.org/x/sync v0.4.0 // indirect
-	golang.org/x/tools v0.14.0 // indirect
+	github.com/muesli/termenv v0.15.2 // indirect
+	github.com/pgavlin/fx v0.1.6 // indirect
+	github.com/pulumi/appdash v0.0.0-20231130102222-75f619a67231 // indirect
+	github.com/pulumi/esc v0.6.1-0.20231111193429-44b746a5b3b5 // indirect
+	golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa // indirect
+	golang.org/x/mod v0.14.0 // indirect
+	golang.org/x/sync v0.5.0 // indirect
+	golang.org/x/tools v0.15.0 // indirect
 	google.golang.org/genproto/googleapis/rpc v0.0.0-20230706204954-ccb25ca9f130 // indirect
 )
 
@@ -60,7 +62,7 @@ require (
 	github.com/inconshreveable/mousetrap v1.1.0 // indirect
 	github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
 	github.com/kevinburke/ssh_config v1.2.0 // indirect
-	github.com/mattn/go-runewidth v0.0.14 // indirect
+	github.com/mattn/go-runewidth v0.0.15 // indirect
 	github.com/mitchellh/go-ps v1.0.0 // indirect
 	github.com/mitchellh/go-wordwrap v1.0.1 // indirect
 	github.com/natefinch/atomic v1.0.1 // indirect
@@ -71,8 +73,8 @@ require (
 	github.com/pkg/errors v0.9.1 // indirect
 	github.com/pkg/term v1.1.0 // indirect
 	github.com/pmezard/go-difflib v1.0.0 // indirect
-	github.com/pulumi/pulumi/pkg/v3 v3.93.0
-	github.com/pulumi/pulumi/sdk/v3 v3.93.0
+	github.com/pulumi/pulumi/pkg/v3 v3.96.2
+	github.com/pulumi/pulumi/sdk/v3 v3.96.2
 	github.com/rivo/uniseg v0.4.4 // indirect
 	github.com/rogpeppe/go-internal v1.11.0 // indirect
 	github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06 // indirect
@@ -90,13 +92,12 @@ require (
 	github.com/xanzy/ssh-agent v0.3.3 // indirect
 	github.com/zclconf/go-cty v1.13.2 // indirect
 	go.uber.org/atomic v1.9.0 // indirect
-	golang.org/x/crypto v0.14.0 // indirect
-	golang.org/x/net v0.17.0 // indirect
-	golang.org/x/sys v0.13.0 // indirect
-	golang.org/x/term v0.13.0 // indirect
-	golang.org/x/text v0.13.0 // indirect
+	golang.org/x/crypto v0.15.0 // indirect
+	golang.org/x/net v0.18.0 // indirect
+	golang.org/x/sys v0.14.0 // indirect
+	golang.org/x/term v0.14.0 // indirect
+	golang.org/x/text v0.14.0 // indirect
 	gopkg.in/warnings.v0 v0.1.2 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
 	lukechampine.com/frand v1.4.2 // indirect
-	sourcegraph.com/sourcegraph/appdash v0.0.0-20211028080628-e2786a622600 // indirect
 )
diff --git a/x/muxer/go.sum b/x/muxer/go.sum
index c3849e4c..c68a5162 100644
--- a/x/muxer/go.sum
+++ b/x/muxer/go.sum
@@ -102,26 +102,26 @@ github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVc
 github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=
 github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
 github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
-github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98=
-github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
+github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
+github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
 github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
 github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
 github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
 github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
-github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
-github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
+github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
+github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
 github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc=
 github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg=
 github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=
 github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=
-github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b h1:1XF24mVaiu7u+CFywTdcDo2ie1pzzhwjt6RHqzpMU34=
-github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b/go.mod h1:fQuZ0gauxyBcmsdE3ZT4NasjaRdxmbCS0jRHsrWu3Ho=
+github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI=
+github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo=
 github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
 github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
 github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
 github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
-github.com/muesli/termenv v0.15.1 h1:UzuTb/+hhlBugQz28rpzey4ZuKcZ03MeKsoG7IJZIxs=
-github.com/muesli/termenv v0.15.1/go.mod h1:HeAQPTzpfs016yGtA4g00CsdYnVLJvxsS4ANqrZs2sQ=
+github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo=
+github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8=
 github.com/natefinch/atomic v1.0.1 h1:ZPYKxkqQOx3KZ+RsbnP/YsgvxWQPGxjC0oBt2AhwV0A=
 github.com/natefinch/atomic v1.0.1/go.mod h1:N/D/ELrljoqDyT3rZrsUmtsuzvHkeB/wWjHV22AZRbM=
 github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI=
@@ -130,6 +130,8 @@ github.com/opentracing/basictracer-go v1.1.0/go.mod h1:V2HZueSJEp879yv285Aap1BS6
 github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
 github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs=
 github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
+github.com/pgavlin/fx v0.1.6 h1:r9jEg69DhNoCd3Xh0+5mIbdbS3PqWrVWujkY76MFRTU=
+github.com/pgavlin/fx v0.1.6/go.mod h1:KWZJ6fqBBSh8GxHYqwYCf3rYE7Gp2p0N8tJp8xv9u9M=
 github.com/pgavlin/goldmark v1.1.33-0.20200616210433-b5eb04559386 h1:LoCV5cscNVWyK5ChN/uCoIFJz8jZD63VQiGJIRgr6uo=
 github.com/pgavlin/goldmark v1.1.33-0.20200616210433-b5eb04559386/go.mod h1:MRxHTJrf9FhdfNQ8Hdeh9gmHevC9RJE/fu8M3JIGjoE=
 github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4=
@@ -140,12 +142,14 @@ github.com/pkg/term v1.1.0 h1:xIAAdCMh3QIAy+5FrE8Ad8XoDhEU4ufwbaSozViP9kk=
 github.com/pkg/term v1.1.0/go.mod h1:E25nymQcrSllhX42Ok8MRm1+hyBdHY0dCeiKZ9jpNGw=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/pulumi/esc v0.5.7-0.20231030195049-f71961c0d5fa h1:5y6/zZsPQW8xNgfyWVMTnuSl8gH2wrYkvTOAqPwhM9Q=
-github.com/pulumi/esc v0.5.7-0.20231030195049-f71961c0d5fa/go.mod h1:Y6W21yUukvxS2NnS5ae1beMSPhMvj0xNAYcDqDHVj/g=
-github.com/pulumi/pulumi/pkg/v3 v3.93.0 h1:ryb8a691MffcAv8KAgAIRvpZOjVW1xCpSDH10x9ZYUg=
-github.com/pulumi/pulumi/pkg/v3 v3.93.0/go.mod h1:cGnwvoPZ8fjR0cUvOt1skqLl8E4fIN708lFITPhY/K8=
-github.com/pulumi/pulumi/sdk/v3 v3.93.0 h1:5InTUxulGuPMXQbnrruIbOEJylRkRWfoDyQIj6dtsYA=
-github.com/pulumi/pulumi/sdk/v3 v3.93.0/go.mod h1:zeqyIODqbb6GrEyhFV6aJET/xBSXSnF7Bw/EjbYZUnU=
+github.com/pulumi/appdash v0.0.0-20231130102222-75f619a67231 h1:vkHw5I/plNdTr435cARxCW6q9gc0S/Yxz7Mkd38pOb0=
+github.com/pulumi/appdash v0.0.0-20231130102222-75f619a67231/go.mod h1:murToZ2N9hNJzewjHBgfFdXhZKjY3z5cYC1VXk+lbFE=
+github.com/pulumi/esc v0.6.1-0.20231111193429-44b746a5b3b5 h1:1DJMji9F7XPea46bSuhy4/85n8J4/Mfz8PWLZtjKKiI=
+github.com/pulumi/esc v0.6.1-0.20231111193429-44b746a5b3b5/go.mod h1:Y6W21yUukvxS2NnS5ae1beMSPhMvj0xNAYcDqDHVj/g=
+github.com/pulumi/pulumi/pkg/v3 v3.96.2 h1:d+zDsh4K1j90q/upDRkhgxHkspP20Xxd3g6OaMbw574=
+github.com/pulumi/pulumi/pkg/v3 v3.96.2/go.mod h1:lR+q06XrnEqRzJXZ6IeHb4nu+U5fswFcc51Q0MRBTio=
+github.com/pulumi/pulumi/sdk/v3 v3.96.2 h1:q5ZKdf+e9JR+a6Eiueg0Ohy6jCQGk9pO2V7xI/qGP3I=
+github.com/pulumi/pulumi/sdk/v3 v3.96.2/go.mod h1:yvD23IIRiqIXuo4kaZNe5zK/uT0nhO99wr6BVEqoi7A=
 github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
 github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
 github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
@@ -203,18 +207,18 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y
 golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
 golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
 golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
-golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
-golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
-golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
-golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
+golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA=
+golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g=
+golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ=
+golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE=
 golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
 golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
 golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
 golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
-golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY=
-golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
+golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
+golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
 golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@@ -226,15 +230,15 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug
 golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
 golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
 golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
-golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
-golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
+golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg=
+golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ=
-golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
+golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
+golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -256,15 +260,15 @@ golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
-golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q=
+golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
 golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
 golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
-golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek=
-golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
+golang.org/x/term v0.14.0 h1:LGK9IlZ8T9jvdy6cTdfKUCltatMFOehAQo9SRC46UQ8=
+golang.org/x/term v0.14.0/go.mod h1:TySc+nGkYR6qt8km8wUhuFRTVSMIX3XPR58y2lC8vww=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
@@ -272,8 +276,8 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
 golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
 golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
 golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
-golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
-golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
+golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
+golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
@@ -282,8 +286,8 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY
 golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
 golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
 golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
-golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc=
-golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg=
+golang.org/x/tools v0.15.0 h1:zdAyfUGbYmuVokhzVmghFl2ZJh5QhcfebBgmVPFYA+8=
+golang.org/x/tools v0.15.0/go.mod h1:hpksKq4dtpQWS1uQ61JkdqWM3LscIS6Slf+VVkm+wQk=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -310,5 +314,3 @@ gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 lukechampine.com/frand v1.4.2 h1:RzFIpOvkMXuPMBb9maa4ND4wjBn71E1Jpf8BzJHMaVw=
 lukechampine.com/frand v1.4.2/go.mod h1:4S/TM2ZgrKejMcKMbeLjISpJMO+/eZ1zu3vYX9dtj3s=
 pgregory.net/rapid v0.6.1 h1:4eyrDxyht86tT4Ztm+kvlyNBLIk071gR+ZQdhphc9dQ=
-sourcegraph.com/sourcegraph/appdash v0.0.0-20211028080628-e2786a622600 h1:hfyJ5ku9yFtLVOiSxa3IN+dx5eBQT9mPmKFypAmg8XM=
-sourcegraph.com/sourcegraph/appdash v0.0.0-20211028080628-e2786a622600/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU=


Test patch:
diff --git a/internal/testing/schemas.go b/internal/testing/schemas.go
index add931b0..5aa5a772 100644
--- a/internal/testing/schemas.go
+++ b/internal/testing/schemas.go
@@ -60,14 +60,14 @@ func AssertEqualsJSONFile[T any](
 	assertDataMatchesFile := func(actualData T, file string) {
 		expectedData, err := readTFromFile(file)
 		assert.NoError(t, err)
-		assert.Equal(t, tToString(expectedData), tToString(actualData))
+		assert.JSONEq(t, tToString(expectedData), tToString(actualData))
 	}
 
 	if os.Getenv("PULUMI_ACCEPT") != "" {
 		buf := bytes.Buffer{}
 		err := marshalT(actualData, &buf)
 		assert.NoError(t, err)
-		err = os.WriteFile(expectedJSONFile, buf.Bytes(), 0600)
+		err = os.WriteFile(expectedJSONFile, buf.Bytes(), 0o600)
 		assert.NoError(t, err)
 	}
 
diff --git a/internal/testprovider/schema.go b/internal/testprovider/schema.go
index f781e675..e78f07fe 100644
--- a/internal/testprovider/schema.go
+++ b/internal/testprovider/schema.go
@@ -621,8 +621,8 @@ func ProviderV2() *schemav2.Provider {
 				},
 				Importer: &schemav2.ResourceImporter{
 					StateContext: func(_ context.Context, state *schemav2.ResourceData,
-						_ interface{}) ([]*schemav2.ResourceData, error) {
-
+						_ interface{},
+					) ([]*schemav2.ResourceData, error) {
 						return []*schemav2.ResourceData{state}, nil
 					},
 				},
@@ -868,8 +868,8 @@ func AssertProvider(f func(data *schemav2.ResourceData)) *schemav2.Provider {
 				},
 				Importer: &schemav2.ResourceImporter{
 					StateContext: func(_ context.Context, state *schemav2.ResourceData,
-						_ interface{}) ([]*schemav2.ResourceData, error) {
-
+						_ interface{},
+					) ([]*schemav2.ResourceData, error) {
 						return []*schemav2.ResourceData{state}, nil
 					},
 				},
@@ -911,3 +911,21 @@ func CustomizedDiffProvider(f func(data *schemav2.ResourceData)) *schemav2.Provi
 		},
 	}
 }
+
+func MaxItemsOneProvider() *schemav2.Provider {
+	return &schemav2.Provider{
+		Schema: map[string]*schemav2.Schema{},
+		ResourcesMap: map[string]*schemav2.Resource{
+			"nested_str_res": {
+				Schema: map[string]*schemav2.Schema{
+					"nested_str": {
+						Type:     schemav2.TypeList,
+						MaxItems: 1,
+						Elem:     &schemav2.Schema{Type: schemav2.TypeString},
+						Optional: true,
+					},
+				},
+			},
+		},
+	}
+}
diff --git a/pf/internal/defaults/defaults_test.go b/pf/internal/defaults/defaults_test.go
index a2cfd082..c900e389 100644
--- a/pf/internal/defaults/defaults_test.go
+++ b/pf/internal/defaults/defaults_test.go
@@ -24,7 +24,7 @@ import (
 	"github.com/pulumi/pulumi/sdk/v3/go/common/resource"
 
 	"github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tfbridge"
-	"github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tfshim"
+	shim "github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tfshim"
 	"github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tfshim/schema"
 )
 
@@ -62,7 +62,7 @@ func TestApplyDefaultInfoValues(t *testing.T) {
 	}
 
 	testFrom := func(res *tfbridge.PulumiResource) (interface{}, error) {
-		n := string(res.URN.Name()) + "-"
+		n := res.URN.Name() + "-"
 		a := []rune("12345")
 		unique, err := resource.NewUniqueName(res.Seed, n, 3, 12, a)
 		return resource.NewStringProperty(unique), err
@@ -74,7 +74,7 @@ func TestApplyDefaultInfoValues(t *testing.T) {
 	) func(context.Context, tfbridge.ComputeDefaultOptions) (interface{}, error) {
 		return func(_ context.Context, opts tfbridge.ComputeDefaultOptions) (interface{}, error) {
 			require.Equal(t, expectPriorValue, opts.PriorValue)
-			n := string(opts.URN.Name()) + "-"
+			n := opts.URN.Name() + "-"
 			a := []rune("12345")
 			unique, err := resource.NewUniqueName(opts.Seed, n, 3, 12, a)
 			return resource.NewStringProperty(unique), err
diff --git a/unstable/logging/logging_test.go b/pf/internal/logging/logging_test.go
similarity index 79%
rename from unstable/logging/logging_test.go
rename to pf/internal/logging/logging_test.go
index a7216826..cdbb40cd 100644
--- a/unstable/logging/logging_test.go
+++ b/pf/internal/logging/logging_test.go
@@ -26,6 +26,8 @@ import (
 
 	"github.com/pulumi/pulumi/sdk/v3/go/common/diag"
 	"github.com/pulumi/pulumi/sdk/v3/go/common/resource"
+
+	"github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tfbridge"
 )
 
 func TestLogging(t *testing.T) {
@@ -36,7 +38,6 @@ func TestLogging(t *testing.T) {
 		opts LogOptions
 		emit func(context.Context)
 		logs []log
-		env  map[string]string
 	}{
 		{
 			name: "WARN and higher propagates by default",
@@ -59,58 +60,6 @@ func TestLogging(t *testing.T) {
 				},
 			},
 		},
-		{
-			name: "TF_LOG env var filtering can restrict logs",
-			opts: LogOptions{},
-			emit: func(ctx context.Context) {
-				tflog.Trace(ctx, "Something went wrong TRACE")
-				tflog.Debug(ctx, "Something went wrong DEBUG")
-				tflog.Info(ctx, "Something went wrong INFO")
-				tflog.Warn(ctx, "Something went wrong WARN")
-				tflog.Error(ctx, "Something went wrong ERROR ")
-			},
-			env: map[string]string{
-				"TF_LOG": "ERROR",
-			},
-			logs: []log{
-				{
-					msg: `Something went wrong ERROR`,
-					sev: diag.Error,
-				},
-			},
-		},
-		{
-			name: "TF_LOG env var filtering can enable more logging",
-			opts: LogOptions{},
-			emit: func(ctx context.Context) {
-				tflog.Trace(ctx, "Something went wrong TRACE")
-				tflog.Debug(ctx, "Something went wrong DEBUG")
-				tflog.Info(ctx, "Something went wrong INFO")
-				tflog.Warn(ctx, "Something went wrong WARN")
-				tflog.Error(ctx, "Something went wrong ERROR ")
-			},
-			env: map[string]string{
-				"TF_LOG": "DEBUG",
-			},
-			logs: []log{
-				{
-					msg: `Something went wrong DEBUG`,
-					sev: diag.Debug,
-				},
-				{
-					msg: `Something went wrong INFO`,
-					sev: diag.Info,
-				},
-				{
-					msg: `Something went wrong WARN`,
-					sev: diag.Warning,
-				},
-				{
-					msg: `Something went wrong ERROR`,
-					sev: diag.Error,
-				},
-			},
-		},
 		{
 			name: "URN propagates when set",
 			opts: LogOptions{URN: urn},
@@ -139,7 +88,7 @@ func TestLogging(t *testing.T) {
 			name: "User Logging",
 			opts: LogOptions{URN: urn},
 			emit: func(ctx context.Context) {
-				log := getLogger(ctx)
+				log := tfbridge.GetLogger(ctx)
 				log.Warn("warn")
 				log.Status().Info("info - status")
 
@@ -164,10 +113,6 @@ func TestLogging(t *testing.T) {
 		c := c
 
 		t.Run(c.name, func(t *testing.T) {
-			for k, v := range c.env {
-				t.Setenv(k, v)
-			}
-
 			ctx := context.Background()
 			opts := c.opts
 			s := &testLogSink{}
@@ -233,7 +178,7 @@ type testLogSink struct {
 	logs []log
 }
 
-var _ Sink = &testLogSink{}
+var _ LogSink = &testLogSink{}
 
 func (sink *testLogSink) Log(context context.Context, sev diag.Severity, urn resource.URN, msg string) error {
 	sink.logs = append(sink.logs, log{
diff --git a/pf/tests/go.mod b/pf/tests/go.mod
index f40a3558..ab50b3fa 100644
--- a/pf/tests/go.mod
+++ b/pf/tests/go.mod
@@ -9,7 +9,7 @@ require (
 	github.com/hashicorp/terraform-provider-tls/shim v0.0.0-00010101000000-000000000000
 	github.com/pulumi/pulumi-terraform-bridge/pf v0.0.0
 	github.com/pulumi/pulumi-terraform-bridge/testing v0.0.1
-	github.com/pulumi/pulumi-terraform-bridge/v3 v3.66.0
+	github.com/pulumi/pulumi-terraform-bridge/v3 v3.68.0
 	github.com/stretchr/testify v1.8.4
 	github.com/terraform-providers/terraform-provider-random/randomshim v0.0.0
 )
@@ -26,20 +26,23 @@ require (
 	github.com/charmbracelet/lipgloss v0.7.1 // indirect
 	github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect
 	github.com/cyphar/filepath-securejoin v0.2.4 // indirect
+	github.com/deckarep/golang-set/v2 v2.5.0 // indirect
 	github.com/google/s2a-go v0.1.4 // indirect
 	github.com/hashicorp/logutils v1.0.0 // indirect
 	github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
 	github.com/mattn/go-localereader v0.0.1 // indirect
-	github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b // indirect
+	github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
 	github.com/muesli/reflow v0.3.0 // indirect
-	github.com/muesli/termenv v0.15.1 // indirect
-	github.com/pulumi/esc v0.6.0 // indirect
+	github.com/muesli/termenv v0.15.2 // indirect
+	github.com/pgavlin/fx v0.1.6 // indirect
+	github.com/pulumi/appdash v0.0.0-20231130102222-75f619a67231 // indirect
+	github.com/pulumi/esc v0.6.1-0.20231111193429-44b746a5b3b5 // indirect
 	github.com/pulumi/terraform-diff-reader v0.0.2 // indirect
 	github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect
 	github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect
 	github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
-	golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect
-	golang.org/x/tools v0.14.0 // indirect
+	golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa // indirect
+	golang.org/x/tools v0.15.0 // indirect
 	google.golang.org/genproto/googleapis/api v0.0.0-20230706204954-ccb25ca9f130 // indirect
 	google.golang.org/genproto/googleapis/rpc v0.0.0-20230706204954-ccb25ca9f130 // indirect
 )
@@ -172,8 +175,8 @@ require (
 	github.com/kevinburke/ssh_config v1.2.0 // indirect
 	github.com/klauspost/compress v1.15.11 // indirect
 	github.com/mattn/go-colorable v0.1.12 // indirect
-	github.com/mattn/go-isatty v0.0.18 // indirect
-	github.com/mattn/go-runewidth v0.0.14 // indirect
+	github.com/mattn/go-isatty v0.0.19 // indirect
+	github.com/mattn/go-runewidth v0.0.15 // indirect
 	github.com/mitchellh/cli v1.1.5 // indirect
 	github.com/mitchellh/copystructure v1.2.0 // indirect
 	github.com/mitchellh/go-homedir v1.1.0 // indirect
@@ -199,9 +202,9 @@ require (
 	github.com/posener/complete v1.2.3 // indirect
 	github.com/pulumi/pulumi-java/pkg v0.9.8 // indirect
 	github.com/pulumi/pulumi-terraform-bridge/x/muxer v0.0.6 // indirect
-	github.com/pulumi/pulumi-yaml v1.4.2 // indirect
-	github.com/pulumi/pulumi/pkg/v3 v3.94.2
-	github.com/pulumi/pulumi/sdk/v3 v3.94.2
+	github.com/pulumi/pulumi-yaml v1.4.3 // indirect
+	github.com/pulumi/pulumi/pkg/v3 v3.96.2
+	github.com/pulumi/pulumi/sdk/v3 v3.96.2
 	github.com/pulumi/schema-tools v0.1.2 // indirect
 	github.com/rivo/uniseg v0.4.4 // indirect
 	github.com/rogpeppe/go-internal v1.11.0 // indirect
@@ -233,14 +236,14 @@ require (
 	go.uber.org/atomic v1.9.0 // indirect
 	gocloud.dev v0.27.0 // indirect
 	gocloud.dev/secrets/hashivault v0.27.0 // indirect
-	golang.org/x/crypto v0.14.0 // indirect
-	golang.org/x/mod v0.13.0 // indirect
-	golang.org/x/net v0.17.0 // indirect
+	golang.org/x/crypto v0.15.0 // indirect
+	golang.org/x/mod v0.14.0 // indirect
+	golang.org/x/net v0.18.0 // indirect
 	golang.org/x/oauth2 v0.8.0 // indirect
-	golang.org/x/sync v0.4.0 // indirect
-	golang.org/x/sys v0.13.0 // indirect
-	golang.org/x/term v0.13.0 // indirect
-	golang.org/x/text v0.13.0 // indirect
+	golang.org/x/sync v0.5.0 // indirect
+	golang.org/x/sys v0.14.0 // indirect
+	golang.org/x/term v0.14.0 // indirect
+	golang.org/x/text v0.14.0 // indirect
 	golang.org/x/time v0.3.0 // indirect
 	golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
 	google.golang.org/api v0.126.0 // indirect
@@ -252,7 +255,7 @@ require (
 	gopkg.in/warnings.v0 v0.1.2 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
 	lukechampine.com/frand v1.4.2 // indirect
-	sourcegraph.com/sourcegraph/appdash v0.0.0-20211028080628-e2786a622600 // indirect
+	sourcegraph.com/sourcegraph/appdash v0.0.0-20211028080628-e2786a622600
 )
 
 replace github.com/hashicorp/terraform-plugin-sdk/v2 => github.com/pulumi/terraform-plugin-sdk/v2 v2.0.0-20230912190043-e6d96b3b8f7e
diff --git a/pf/tests/go.sum b/pf/tests/go.sum
index 62f3b3b4..7a9f9c26 100644
--- a/pf/tests/go.sum
+++ b/pf/tests/go.sum
@@ -1095,6 +1095,8 @@ github.com/d2g/hardwareaddr v0.0.0-20190221164911-e7d9fbe030e4/go.mod h1:bMl4RjI
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/deckarep/golang-set/v2 v2.5.0 h1:hn6cEZtQ0h3J8kFrHR/NrzyOoTnjgW1+FmNJzQ7y/sA=
+github.com/deckarep/golang-set/v2 v2.5.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4=
 github.com/denisenkom/go-mssqldb v0.12.2/go.mod h1:lnIw1mZukFRZDJYQ0Pb833QS2IaC3l5HkEfra2LJ+sk=
 github.com/dennwc/varint v1.0.0/go.mod h1:hnItb35rvZvJrbTALZtY/iQfDs48JKRG1RPpgziApxA=
 github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0=
@@ -1866,15 +1868,15 @@ github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOA
 github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
 github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
 github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
-github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98=
-github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
+github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
+github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
 github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
 github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
 github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
 github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
 github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
-github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
-github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
+github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
+github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
 github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
 github.com/mattn/go-shellwords v1.0.6/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
 github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
@@ -1954,14 +1956,14 @@ github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJ
 github.com/montanaflynn/stats v0.6.6/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
 github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
 github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ=
-github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b h1:1XF24mVaiu7u+CFywTdcDo2ie1pzzhwjt6RHqzpMU34=
-github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b/go.mod h1:fQuZ0gauxyBcmsdE3ZT4NasjaRdxmbCS0jRHsrWu3Ho=
+github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI=
+github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo=
 github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
 github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
 github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
 github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
-github.com/muesli/termenv v0.15.1 h1:UzuTb/+hhlBugQz28rpzey4ZuKcZ03MeKsoG7IJZIxs=
-github.com/muesli/termenv v0.15.1/go.mod h1:HeAQPTzpfs016yGtA4g00CsdYnVLJvxsS4ANqrZs2sQ=
+github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo=
+github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8=
 github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
 github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
 github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
@@ -2062,6 +2064,8 @@ github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrap
 github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
 github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac=
 github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
+github.com/pgavlin/fx v0.1.6 h1:r9jEg69DhNoCd3Xh0+5mIbdbS3PqWrVWujkY76MFRTU=
+github.com/pgavlin/fx v0.1.6/go.mod h1:KWZJ6fqBBSh8GxHYqwYCf3rYE7Gp2p0N8tJp8xv9u9M=
 github.com/pgavlin/goldmark v1.1.33-0.20200616210433-b5eb04559386 h1:LoCV5cscNVWyK5ChN/uCoIFJz8jZD63VQiGJIRgr6uo=
 github.com/pgavlin/goldmark v1.1.33-0.20200616210433-b5eb04559386/go.mod h1:MRxHTJrf9FhdfNQ8Hdeh9gmHevC9RJE/fu8M3JIGjoE=
 github.com/phpdave11/gofpdf v1.4.2/go.mod h1:zpO6xFn9yxo3YLyMvW8HcKWVdbNqgIfOOp2dXMnm1mY=
@@ -2152,16 +2156,18 @@ github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1
 github.com/prometheus/prometheus v0.35.0/go.mod h1:7HaLx5kEPKJ0GDgbODG0fZgXbQ8K/XjZNJXQmbmgQlY=
 github.com/prometheus/prometheus v0.37.0/go.mod h1:egARUgz+K93zwqsVIAneFlLZefyGOON44WyAp4Xqbbk=
 github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
-github.com/pulumi/esc v0.6.0 h1:m8jWgBektlj26RvrN3/sM0r1lYiwTMmqVPvLxCxahGE=
-github.com/pulumi/esc v0.6.0/go.mod h1:Y6W21yUukvxS2NnS5ae1beMSPhMvj0xNAYcDqDHVj/g=
+github.com/pulumi/appdash v0.0.0-20231130102222-75f619a67231 h1:vkHw5I/plNdTr435cARxCW6q9gc0S/Yxz7Mkd38pOb0=
+github.com/pulumi/appdash v0.0.0-20231130102222-75f619a67231/go.mod h1:murToZ2N9hNJzewjHBgfFdXhZKjY3z5cYC1VXk+lbFE=
+github.com/pulumi/esc v0.6.1-0.20231111193429-44b746a5b3b5 h1:1DJMji9F7XPea46bSuhy4/85n8J4/Mfz8PWLZtjKKiI=
+github.com/pulumi/esc v0.6.1-0.20231111193429-44b746a5b3b5/go.mod h1:Y6W21yUukvxS2NnS5ae1beMSPhMvj0xNAYcDqDHVj/g=
 github.com/pulumi/pulumi-java/pkg v0.9.8 h1:c8mYsalnRXA2Ibgvv6scefOn6mW1Vb0UT0mcDqjsivQ=
 github.com/pulumi/pulumi-java/pkg v0.9.8/go.mod h1:c6rSw/+q4O0IImgJ9axxoC6QesbPYWBaG5gimbHouUQ=
-github.com/pulumi/pulumi-yaml v1.4.2 h1:LKptNH2lG6f5IjJmR7bvu7ouBE4sgkTRNd4I8ZHmjHw=
-github.com/pulumi/pulumi-yaml v1.4.2/go.mod h1:1hwsXFnT7hm2ZeftY5fzjkngjs2eT/rv9MsPE48Leb4=
-github.com/pulumi/pulumi/pkg/v3 v3.94.2 h1:b+gQ+EJZMYfSIu/sb/+p36VmfZ2ZJWbEWMsptt2f6Kk=
-github.com/pulumi/pulumi/pkg/v3 v3.94.2/go.mod h1:aCeDMlncyDwX3RG6+vYUjWsy6bOEtoHUY0twCcn5qqE=
-github.com/pulumi/pulumi/sdk/v3 v3.94.2 h1:B8W3CCzA2fX/0730o/7KQXdmqrY5KdgGtiAA6rDMVlk=
-github.com/pulumi/pulumi/sdk/v3 v3.94.2/go.mod h1:zeqyIODqbb6GrEyhFV6aJET/xBSXSnF7Bw/EjbYZUnU=
+github.com/pulumi/pulumi-yaml v1.4.3 h1:GO36c7FTl8If20Dn/w2Hi3huP7kmsO00KNaz3GJU0Ws=
+github.com/pulumi/pulumi-yaml v1.4.3/go.mod h1:1hwsXFnT7hm2ZeftY5fzjkngjs2eT/rv9MsPE48Leb4=
+github.com/pulumi/pulumi/pkg/v3 v3.96.2 h1:d+zDsh4K1j90q/upDRkhgxHkspP20Xxd3g6OaMbw574=
+github.com/pulumi/pulumi/pkg/v3 v3.96.2/go.mod h1:lR+q06XrnEqRzJXZ6IeHb4nu+U5fswFcc51Q0MRBTio=
+github.com/pulumi/pulumi/sdk/v3 v3.96.2 h1:q5ZKdf+e9JR+a6Eiueg0Ohy6jCQGk9pO2V7xI/qGP3I=
+github.com/pulumi/pulumi/sdk/v3 v3.96.2/go.mod h1:yvD23IIRiqIXuo4kaZNe5zK/uT0nhO99wr6BVEqoi7A=
 github.com/pulumi/schema-tools v0.1.2 h1:Fd9xvUjgck4NA+7/jSk7InqCUT4Kj940+EcnbQKpfZo=
 github.com/pulumi/schema-tools v0.1.2/go.mod h1:62lgj52Tzq11eqWTIaKd+EVyYAu5dEcDJxMhTjvMO/k=
 github.com/pulumi/terraform-diff-reader v0.0.2 h1:kTE4nEXU3/SYXESvAIem+wyHMI3abqkI3OhJ0G04LLI=
@@ -2544,8 +2550,8 @@ golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU
 golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
 golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
 golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
-golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
-golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
+golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA=
+golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g=
 golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@@ -2561,8 +2567,8 @@ golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u0
 golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
 golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
 golang.org/x/exp v0.0.0-20220827204233-334a2380cb91/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
-golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
-golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
+golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ=
+golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE=
 golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
 golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
 golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
@@ -2608,8 +2614,8 @@ golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
 golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
 golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
 golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
-golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY=
-golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
+golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
+golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
 golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -2706,8 +2712,8 @@ golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
 golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
 golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
 golang.org/x/net v0.13.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
-golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
-golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
+golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg=
+golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -2761,8 +2767,8 @@ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJ
 golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ=
-golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
+golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
+golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
 golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -2923,8 +2929,8 @@ golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
-golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q=
+golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
@@ -2941,8 +2947,8 @@ golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
 golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
 golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o=
 golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
-golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek=
-golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
+golang.org/x/term v0.14.0 h1:LGK9IlZ8T9jvdy6cTdfKUCltatMFOehAQo9SRC46UQ8=
+golang.org/x/term v0.14.0/go.mod h1:TySc+nGkYR6qt8km8wUhuFRTVSMIX3XPR58y2lC8vww=
 golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -2960,8 +2966,9 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
 golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
 golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
 golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
-golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
 golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
+golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
+golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
 golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -3070,8 +3077,8 @@ golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA=
 golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k=
 golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
 golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s=
-golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc=
-golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg=
+golang.org/x/tools v0.15.0 h1:zdAyfUGbYmuVokhzVmghFl2ZJh5QhcfebBgmVPFYA+8=
+golang.org/x/tools v0.15.0/go.mod h1:hpksKq4dtpQWS1uQ61JkdqWM3LscIS6Slf+VVkm+wQk=
 golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
diff --git a/pf/tests/testdatagen/genrandom/go.mod b/pf/tests/testdatagen/genrandom/go.mod
index 7680f8b9..daaf46b5 100644
--- a/pf/tests/testdatagen/genrandom/go.mod
+++ b/pf/tests/testdatagen/genrandom/go.mod
@@ -25,7 +25,9 @@ require (
 	github.com/muesli/cancelreader v0.2.2 // indirect
 	github.com/muesli/reflow v0.3.0 // indirect
 	github.com/muesli/termenv v0.15.1 // indirect
+	github.com/pgavlin/fx v0.1.6 // indirect
 	github.com/pjbgf/sha1cd v0.3.0 // indirect
+	github.com/pulumi/appdash v0.0.0-20231130102222-75f619a67231 // indirect
 	github.com/pulumi/esc v0.5.6 // indirect
 	github.com/skeema/knownhosts v1.2.0 // indirect
 	github.com/stretchr/objx v0.5.0 // indirect
@@ -65,7 +67,7 @@ require (
 	github.com/opentracing/opentracing-go v1.2.0 // indirect
 	github.com/pkg/errors v0.9.1 // indirect
 	github.com/pkg/term v1.1.0 // indirect
-	github.com/pulumi/pulumi/sdk/v3 v3.93.0
+	github.com/pulumi/pulumi/sdk/v3 v3.96.2
 	github.com/rivo/uniseg v0.4.4 // indirect
 	github.com/rogpeppe/go-internal v1.11.0 // indirect
 	github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06 // indirect
@@ -90,7 +92,6 @@ require (
 	gopkg.in/warnings.v0 v0.1.2 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
 	lukechampine.com/frand v1.4.2 // indirect
-	sourcegraph.com/sourcegraph/appdash v0.0.0-20211028080628-e2786a622600 // indirect
 )
 
 replace github.com/hashicorp/terraform-plugin-sdk/v2 => github.com/pulumi/terraform-plugin-sdk/v2 v2.0.0-20230912190043-e6d96b3b8f7e
diff --git a/pf/tests/testdatagen/genrandom/go.sum b/pf/tests/testdatagen/genrandom/go.sum
index fb6a87ea..4ac3540a 100644
--- a/pf/tests/testdatagen/genrandom/go.sum
+++ b/pf/tests/testdatagen/genrandom/go.sum
@@ -126,6 +126,8 @@ github.com/opentracing/basictracer-go v1.1.0/go.mod h1:V2HZueSJEp879yv285Aap1BS6
 github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
 github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs=
 github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
+github.com/pgavlin/fx v0.1.6 h1:r9jEg69DhNoCd3Xh0+5mIbdbS3PqWrVWujkY76MFRTU=
+github.com/pgavlin/fx v0.1.6/go.mod h1:KWZJ6fqBBSh8GxHYqwYCf3rYE7Gp2p0N8tJp8xv9u9M=
 github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4=
 github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI=
 github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
@@ -134,12 +136,14 @@ github.com/pkg/term v1.1.0 h1:xIAAdCMh3QIAy+5FrE8Ad8XoDhEU4ufwbaSozViP9kk=
 github.com/pkg/term v1.1.0/go.mod h1:E25nymQcrSllhX42Ok8MRm1+hyBdHY0dCeiKZ9jpNGw=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/pulumi/appdash v0.0.0-20231130102222-75f619a67231 h1:vkHw5I/plNdTr435cARxCW6q9gc0S/Yxz7Mkd38pOb0=
+github.com/pulumi/appdash v0.0.0-20231130102222-75f619a67231/go.mod h1:murToZ2N9hNJzewjHBgfFdXhZKjY3z5cYC1VXk+lbFE=
 github.com/pulumi/esc v0.5.6 h1:4WV3X7OEVcChIwbSG+JxhZDdmq/q7lFPaSjHRYlPwmI=
 github.com/pulumi/esc v0.5.6/go.mod h1:wpwNfVS5fV7Kd51j4dJ6FWYlKfxdqyppgp0gtkzqH04=
 github.com/pulumi/pulumi-random/sdk/v4 v4.8.2 h1:ZlXB3mx1YvAjs+jm59rcpvfl1J7dpLOBOxUb5vEPkZk=
 github.com/pulumi/pulumi-random/sdk/v4 v4.8.2/go.mod h1:czSwj+jZnn/VWovMpTLUs/RL/ZS4PFHRdmlXrkvHqeI=
-github.com/pulumi/pulumi/sdk/v3 v3.93.0 h1:5InTUxulGuPMXQbnrruIbOEJylRkRWfoDyQIj6dtsYA=
-github.com/pulumi/pulumi/sdk/v3 v3.93.0/go.mod h1:zeqyIODqbb6GrEyhFV6aJET/xBSXSnF7Bw/EjbYZUnU=
+github.com/pulumi/pulumi/sdk/v3 v3.96.2 h1:q5ZKdf+e9JR+a6Eiueg0Ohy6jCQGk9pO2V7xI/qGP3I=
+github.com/pulumi/pulumi/sdk/v3 v3.96.2/go.mod h1:yvD23IIRiqIXuo4kaZNe5zK/uT0nhO99wr6BVEqoi7A=
 github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
 github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
 github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
@@ -304,5 +308,3 @@ gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 lukechampine.com/frand v1.4.2 h1:RzFIpOvkMXuPMBb9maa4ND4wjBn71E1Jpf8BzJHMaVw=
 lukechampine.com/frand v1.4.2/go.mod h1:4S/TM2ZgrKejMcKMbeLjISpJMO+/eZ1zu3vYX9dtj3s=
 pgregory.net/rapid v0.5.5 h1:jkgx1TjbQPD/feRoK+S/mXw9e1uj6WilpHrXJowi6oA=
-sourcegraph.com/sourcegraph/appdash v0.0.0-20211028080628-e2786a622600 h1:hfyJ5ku9yFtLVOiSxa3IN+dx5eBQT9mPmKFypAmg8XM=
-sourcegraph.com/sourcegraph/appdash v0.0.0-20211028080628-e2786a622600/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU=
diff --git a/pkg/tests/go.mod b/pkg/tests/go.mod
index ae2cf185..fd176a42 100644
--- a/pkg/tests/go.mod
+++ b/pkg/tests/go.mod
@@ -26,18 +26,21 @@ require (
 	github.com/charmbracelet/lipgloss v0.7.1 // indirect
 	github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect
 	github.com/cyphar/filepath-securejoin v0.2.4 // indirect
+	github.com/deckarep/golang-set/v2 v2.5.0 // indirect
 	github.com/frankban/quicktest v1.14.3 // indirect
 	github.com/google/s2a-go v0.1.4 // indirect
 	github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
 	github.com/mattn/go-localereader v0.0.1 // indirect
-	github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b // indirect
+	github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
 	github.com/muesli/reflow v0.3.0 // indirect
-	github.com/muesli/termenv v0.15.1 // indirect
-	github.com/pulumi/esc v0.6.0 // indirect
+	github.com/muesli/termenv v0.15.2 // indirect
+	github.com/pgavlin/fx v0.1.6 // indirect
+	github.com/pulumi/appdash v0.0.0-20231130102222-75f619a67231 // indirect
+	github.com/pulumi/esc v0.6.1-0.20231111193429-44b746a5b3b5 // indirect
 	github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect
 	github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
-	golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect
-	golang.org/x/tools v0.14.0 // indirect
+	golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa // indirect
+	golang.org/x/tools v0.15.0 // indirect
 	google.golang.org/genproto/googleapis/api v0.0.0-20230706204954-ccb25ca9f130 // indirect
 	google.golang.org/genproto/googleapis/rpc v0.0.0-20230706204954-ccb25ca9f130 // indirect
 )
@@ -141,8 +144,8 @@ require (
 	github.com/json-iterator/go v1.1.12 // indirect
 	github.com/kevinburke/ssh_config v1.2.0 // indirect
 	github.com/mattn/go-colorable v0.1.12 // indirect
-	github.com/mattn/go-isatty v0.0.18 // indirect
-	github.com/mattn/go-runewidth v0.0.14 // indirect
+	github.com/mattn/go-isatty v0.0.19 // indirect
+	github.com/mattn/go-runewidth v0.0.15 // indirect
 	github.com/mitchellh/copystructure v1.2.0 // indirect
 	github.com/mitchellh/go-homedir v1.1.0 // indirect
 	github.com/mitchellh/go-ps v1.0.0 // indirect
@@ -164,8 +167,8 @@ require (
 	github.com/pkg/term v1.1.0 // indirect
 	github.com/pmezard/go-difflib v1.0.0 // indirect
 	github.com/pulumi/pulumi-terraform-bridge/x/muxer v0.0.4 // indirect
-	github.com/pulumi/pulumi/pkg/v3 v3.94.2
-	github.com/pulumi/pulumi/sdk/v3 v3.94.2
+	github.com/pulumi/pulumi/pkg/v3 v3.96.2
+	github.com/pulumi/pulumi/sdk/v3 v3.96.2
 	github.com/pulumi/terraform-diff-reader v0.0.2 // indirect
 	github.com/rivo/uniseg v0.4.4 // indirect
 	github.com/rogpeppe/go-internal v1.11.0 // indirect
@@ -192,14 +195,14 @@ require (
 	go.uber.org/atomic v1.9.0 // indirect
 	gocloud.dev v0.27.0 // indirect
 	gocloud.dev/secrets/hashivault v0.27.0 // indirect
-	golang.org/x/crypto v0.14.0 // indirect
-	golang.org/x/mod v0.13.0 // indirect
-	golang.org/x/net v0.17.0 // indirect
+	golang.org/x/crypto v0.15.0 // indirect
+	golang.org/x/mod v0.14.0 // indirect
+	golang.org/x/net v0.18.0 // indirect
 	golang.org/x/oauth2 v0.8.0 // indirect
-	golang.org/x/sync v0.4.0 // indirect
-	golang.org/x/sys v0.13.0 // indirect
-	golang.org/x/term v0.13.0 // indirect
-	golang.org/x/text v0.13.0 // indirect
+	golang.org/x/sync v0.5.0 // indirect
+	golang.org/x/sys v0.14.0 // indirect
+	golang.org/x/term v0.14.0 // indirect
+	golang.org/x/text v0.14.0 // indirect
 	golang.org/x/time v0.3.0 // indirect
 	golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
 	google.golang.org/api v0.126.0 // indirect
@@ -211,7 +214,6 @@ require (
 	gopkg.in/warnings.v0 v0.1.2 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
 	lukechampine.com/frand v1.4.2 // indirect
-	sourcegraph.com/sourcegraph/appdash v0.0.0-20211028080628-e2786a622600 // indirect
 )
 
 replace github.com/hashicorp/terraform-plugin-sdk/v2 => github.com/pulumi/terraform-plugin-sdk/v2 v2.0.0-20230912190043-e6d96b3b8f7e
diff --git a/pkg/tests/go.sum b/pkg/tests/go.sum
index b0778dda..112edb44 100644
--- a/pkg/tests/go.sum
+++ b/pkg/tests/go.sum
@@ -1090,6 +1090,8 @@ github.com/d2g/hardwareaddr v0.0.0-20190221164911-e7d9fbe030e4/go.mod h1:bMl4RjI
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/deckarep/golang-set/v2 v2.5.0 h1:hn6cEZtQ0h3J8kFrHR/NrzyOoTnjgW1+FmNJzQ7y/sA=
+github.com/deckarep/golang-set/v2 v2.5.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4=
 github.com/denisenkom/go-mssqldb v0.12.2/go.mod h1:lnIw1mZukFRZDJYQ0Pb833QS2IaC3l5HkEfra2LJ+sk=
 github.com/dennwc/varint v1.0.0/go.mod h1:hnItb35rvZvJrbTALZtY/iQfDs48JKRG1RPpgziApxA=
 github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0=
@@ -1836,15 +1838,15 @@ github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOA
 github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
 github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
 github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
-github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98=
-github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
+github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
+github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
 github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
 github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
 github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
 github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
 github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
-github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
-github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
+github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
+github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
 github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
 github.com/mattn/go-shellwords v1.0.6/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
 github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
@@ -1922,14 +1924,14 @@ github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJ
 github.com/montanaflynn/stats v0.6.6/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
 github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
 github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ=
-github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b h1:1XF24mVaiu7u+CFywTdcDo2ie1pzzhwjt6RHqzpMU34=
-github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b/go.mod h1:fQuZ0gauxyBcmsdE3ZT4NasjaRdxmbCS0jRHsrWu3Ho=
+github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI=
+github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo=
 github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
 github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
 github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
 github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
-github.com/muesli/termenv v0.15.1 h1:UzuTb/+hhlBugQz28rpzey4ZuKcZ03MeKsoG7IJZIxs=
-github.com/muesli/termenv v0.15.1/go.mod h1:HeAQPTzpfs016yGtA4g00CsdYnVLJvxsS4ANqrZs2sQ=
+github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo=
+github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8=
 github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
 github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
 github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
@@ -2029,6 +2031,8 @@ github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrap
 github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
 github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac=
 github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
+github.com/pgavlin/fx v0.1.6 h1:r9jEg69DhNoCd3Xh0+5mIbdbS3PqWrVWujkY76MFRTU=
+github.com/pgavlin/fx v0.1.6/go.mod h1:KWZJ6fqBBSh8GxHYqwYCf3rYE7Gp2p0N8tJp8xv9u9M=
 github.com/pgavlin/goldmark v1.1.33-0.20200616210433-b5eb04559386 h1:LoCV5cscNVWyK5ChN/uCoIFJz8jZD63VQiGJIRgr6uo=
 github.com/pgavlin/goldmark v1.1.33-0.20200616210433-b5eb04559386/go.mod h1:MRxHTJrf9FhdfNQ8Hdeh9gmHevC9RJE/fu8M3JIGjoE=
 github.com/phpdave11/gofpdf v1.4.2/go.mod h1:zpO6xFn9yxo3YLyMvW8HcKWVdbNqgIfOOp2dXMnm1mY=
@@ -2119,12 +2123,14 @@ github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1
 github.com/prometheus/prometheus v0.35.0/go.mod h1:7HaLx5kEPKJ0GDgbODG0fZgXbQ8K/XjZNJXQmbmgQlY=
 github.com/prometheus/prometheus v0.37.0/go.mod h1:egARUgz+K93zwqsVIAneFlLZefyGOON44WyAp4Xqbbk=
 github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
-github.com/pulumi/esc v0.6.0 h1:m8jWgBektlj26RvrN3/sM0r1lYiwTMmqVPvLxCxahGE=
-github.com/pulumi/esc v0.6.0/go.mod h1:Y6W21yUukvxS2NnS5ae1beMSPhMvj0xNAYcDqDHVj/g=
-github.com/pulumi/pulumi/pkg/v3 v3.94.2 h1:b+gQ+EJZMYfSIu/sb/+p36VmfZ2ZJWbEWMsptt2f6Kk=
-github.com/pulumi/pulumi/pkg/v3 v3.94.2/go.mod h1:aCeDMlncyDwX3RG6+vYUjWsy6bOEtoHUY0twCcn5qqE=
-github.com/pulumi/pulumi/sdk/v3 v3.94.2 h1:B8W3CCzA2fX/0730o/7KQXdmqrY5KdgGtiAA6rDMVlk=
-github.com/pulumi/pulumi/sdk/v3 v3.94.2/go.mod h1:zeqyIODqbb6GrEyhFV6aJET/xBSXSnF7Bw/EjbYZUnU=
+github.com/pulumi/appdash v0.0.0-20231130102222-75f619a67231 h1:vkHw5I/plNdTr435cARxCW6q9gc0S/Yxz7Mkd38pOb0=
+github.com/pulumi/appdash v0.0.0-20231130102222-75f619a67231/go.mod h1:murToZ2N9hNJzewjHBgfFdXhZKjY3z5cYC1VXk+lbFE=
+github.com/pulumi/esc v0.6.1-0.20231111193429-44b746a5b3b5 h1:1DJMji9F7XPea46bSuhy4/85n8J4/Mfz8PWLZtjKKiI=
+github.com/pulumi/esc v0.6.1-0.20231111193429-44b746a5b3b5/go.mod h1:Y6W21yUukvxS2NnS5ae1beMSPhMvj0xNAYcDqDHVj/g=
+github.com/pulumi/pulumi/pkg/v3 v3.96.2 h1:d+zDsh4K1j90q/upDRkhgxHkspP20Xxd3g6OaMbw574=
+github.com/pulumi/pulumi/pkg/v3 v3.96.2/go.mod h1:lR+q06XrnEqRzJXZ6IeHb4nu+U5fswFcc51Q0MRBTio=
+github.com/pulumi/pulumi/sdk/v3 v3.96.2 h1:q5ZKdf+e9JR+a6Eiueg0Ohy6jCQGk9pO2V7xI/qGP3I=
+github.com/pulumi/pulumi/sdk/v3 v3.96.2/go.mod h1:yvD23IIRiqIXuo4kaZNe5zK/uT0nhO99wr6BVEqoi7A=
 github.com/pulumi/terraform-diff-reader v0.0.2 h1:kTE4nEXU3/SYXESvAIem+wyHMI3abqkI3OhJ0G04LLI=
 github.com/pulumi/terraform-diff-reader v0.0.2/go.mod h1:sZ9FUzGO+yM41hsQHs/yIcj/Y993qMdBxBU5mpDmAfQ=
 github.com/pulumi/terraform-plugin-sdk/v2 v2.0.0-20230912190043-e6d96b3b8f7e h1:blSirnXqvm8JXLxwxelsBroUNRhOHakDO7cgJUYTdpQ=
@@ -2497,8 +2503,8 @@ golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU
 golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
 golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
 golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
-golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
-golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
+golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA=
+golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g=
 golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@@ -2514,8 +2520,8 @@ golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u0
 golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
 golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
 golang.org/x/exp v0.0.0-20220827204233-334a2380cb91/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
-golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
-golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
+golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ=
+golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE=
 golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
 golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
 golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
@@ -2561,8 +2567,8 @@ golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
 golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
 golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
 golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
-golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY=
-golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
+golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
+golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
 golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -2659,8 +2665,8 @@ golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
 golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
 golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
 golang.org/x/net v0.13.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
-golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
-golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
+golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg=
+golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -2713,8 +2719,8 @@ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJ
 golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ=
-golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
+golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
+golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
 golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -2875,8 +2881,8 @@ golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
-golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q=
+golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
@@ -2893,8 +2899,8 @@ golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
 golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
 golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o=
 golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
-golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek=
-golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
+golang.org/x/term v0.14.0 h1:LGK9IlZ8T9jvdy6cTdfKUCltatMFOehAQo9SRC46UQ8=
+golang.org/x/term v0.14.0/go.mod h1:TySc+nGkYR6qt8km8wUhuFRTVSMIX3XPR58y2lC8vww=
 golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -2912,8 +2918,9 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
 golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
 golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
 golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
-golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
 golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
+golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
+golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
 golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -3022,8 +3029,8 @@ golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA=
 golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k=
 golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
 golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s=
-golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc=
-golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg=
+golang.org/x/tools v0.15.0 h1:zdAyfUGbYmuVokhzVmghFl2ZJh5QhcfebBgmVPFYA+8=
+golang.org/x/tools v0.15.0/go.mod h1:hpksKq4dtpQWS1uQ61JkdqWM3LscIS6Slf+VVkm+wQk=
 golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -3529,5 +3536,3 @@ sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
 sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
 sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
 sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU=
-sourcegraph.com/sourcegraph/appdash v0.0.0-20211028080628-e2786a622600 h1:hfyJ5ku9yFtLVOiSxa3IN+dx5eBQT9mPmKFypAmg8XM=
-sourcegraph.com/sourcegraph/appdash v0.0.0-20211028080628-e2786a622600/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU=
diff --git a/pkg/tfbridge/log_test.go b/pkg/tfbridge/log_test.go
index 5eb39ac7..6343df97 100644
--- a/pkg/tfbridge/log_test.go
+++ b/pkg/tfbridge/log_test.go
@@ -15,20 +15,9 @@
 package tfbridge
 
 import (
-	"bytes"
-	"context"
-	"fmt"
 	"testing"
 
 	"github.com/stretchr/testify/assert"
-
-	"github.com/pulumi/pulumi/sdk/v3/go/common/diag"
-	"github.com/pulumi/pulumi/sdk/v3/go/common/resource"
-	pulumirpc "github.com/pulumi/pulumi/sdk/v3/proto/go"
-
-	"github.com/pulumi/pulumi-terraform-bridge/v3/internal/testprovider"
-	shimv2 "github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tfshim/sdk-v2"
-	"github.com/pulumi/pulumi-terraform-bridge/v3/unstable/logging"
 )
 
 // TestLogDirector ensures that logging redirects to the right place.
@@ -108,44 +97,3 @@ func TestLogRedirector(t *testing.T) {
 	assert.Equal(t, 3, len(warnings))
 	assert.Equal(t, 3, len(errors))
 }
-
-// Check if framework logs emitted by SDKv2 based resources actually are captured by Pulumi.
-func TestLogCapture(t *testing.T) {
-	ctx := context.Background()
-	var logs bytes.Buffer
-
-	ctx = logging.InitLogging(ctx, logging.LogOptions{
-		LogSink: &testLogSink{&logs},
-	})
-
-	p := testprovider.ProviderV2()
-	provider := &Provider{
-		tf:     shimv2.NewProvider(p),
-		config: shimv2.NewSchemaMap(p.Schema),
-	}
-
-	_, err := provider.Configure(ctx, &pulumirpc.ConfigureRequest{})
-	assert.NoError(t, err)
-
-	_, err = provider.Configure(ctx, &pulumirpc.ConfigureRequest{})
-	assert.NoError(t, err)
-
-	// Calling Configure twice actually emits a warning from the framework.
-	assert.Contains(t, logs.String(), "Previously configured provider being re-configured.")
-}
-
-type testLogSink struct {
-	buf *bytes.Buffer
-}
-
-var _ logging.Sink = &testLogSink{}
-
-func (s *testLogSink) Log(context context.Context, sev diag.Severity, urn resource.URN, msg string) error {
-	fmt.Fprintf(s.buf, "[%v] [%v] %s\n", sev, urn, msg)
-	return nil
-}
-
-func (s *testLogSink) LogStatus(context context.Context, sev diag.Severity, urn resource.URN, msg string) error {
-	fmt.Fprintf(s.buf, "[status] [%v] [%v] %s\n", sev, urn, msg)
-	return nil
-}
diff --git a/pkg/tfbridge/provider_test.go b/pkg/tfbridge/provider_test.go
index d7e52921..eeb5fcb9 100644
--- a/pkg/tfbridge/provider_test.go
+++ b/pkg/tfbridge/provider_test.go
@@ -2133,3 +2133,107 @@ func TestTransformFromState(t *testing.T) {
 		}`)
 	})
 }
+
+// This emulates the situation where we migrate from a state without maxItemsOne
+// which would make the property a list
+// into a state with maxItemsOne, which would flatten the type.
+// https://github.com/pulumi/pulumi-aws/issues/3092
+func TestMaxItemOneWrongStateDiff(t *testing.T) {
+	p := testprovider.MaxItemsOneProvider()
+	provider := &Provider{
+		tf:     shimv2.NewProvider(p),
+		config: shimv2.NewSchemaMap(p.Schema),
+		resources: map[tokens.Type]Resource{
+			"NestedStrRes": {
+				TF:     shimv2.NewResource(p.ResourcesMap["nested_str_res"]),
+				TFName: "nested_str_res",
+				Schema: &ResourceInfo{
+					Tok:    "NestedStrRes",
+					Fields: map[string]*SchemaInfo{},
+				},
+			},
+		},
+	}
+	t.Run("DiffListAndVal", func(t *testing.T) {
+		testutils.Replay(t, provider, `
+		{
+			"method": "/pulumirpc.ResourceProvider/Diff",
+			"request": {
+				"urn": "urn:pulumi:dev::teststack::NestedStrRes::exres",
+				"id": "0",
+				"olds": {
+					"nested_str": []
+				},
+				"news": {
+					"nested_str": ""
+				}
+			},
+			"response": {
+				"changes": "DIFF_SOME",
+				"hasDetailedDiff": true
+			}
+		}`)
+	})
+	t.Run("DiffListAndValNonEmpty", func(t *testing.T) {
+		testutils.Replay(t, provider, `
+		{
+			"method": "/pulumirpc.ResourceProvider/Diff",
+			"request": {
+				"urn": "urn:pulumi:dev::teststack::NestedStrRes::exres",
+				"id": "0",
+				"olds": {
+					"nested_str": ["val"]
+				},
+				"news": {
+					"nested_str": "val"
+				}
+			},
+			"response": {
+				"changes": "DIFF_SOME",
+				"hasDetailedDiff": true
+			}
+		}`)
+	})
+
+	// Also check that we don't produce spurious diffs when not necessary.
+	t.Run("DiffValAndValEmpty", func(t *testing.T) {
+		testutils.Replay(t, provider, `
+		{
+			"method": "/pulumirpc.ResourceProvider/Diff",
+			"request": {
+				"urn": "urn:pulumi:dev::teststack::NestedStrRes::exres",
+				"id": "0",
+				"olds": {
+					"nested_str": ""
+				},
+				"news": {
+					"nested_str": ""
+				}
+			},
+			"response": {
+				"changes": "DIFF_NONE",
+				"hasDetailedDiff": true
+			}
+		}`)
+	})
+	t.Run("DiffValAndValNonempty", func(t *testing.T) {
+		testutils.Replay(t, provider, `
+		{
+			"method": "/pulumirpc.ResourceProvider/Diff",
+			"request": {
+				"urn": "urn:pulumi:dev::teststack::NestedStrRes::exres",
+				"id": "0",
+				"olds": {
+					"nested_str": "val"
+				},
+				"news": {
+					"nested_str": "val"
+				}
+			},
+			"response": {
+				"changes": "DIFF_NONE",
+				"hasDetailedDiff": true
+			}
+		}`)
+	})
+}
diff --git a/pkg/tfbridge/schema_test.go b/pkg/tfbridge/schema_test.go
index 1d622f56..7d1f75e2 100644
--- a/pkg/tfbridge/schema_test.go
+++ b/pkg/tfbridge/schema_test.go
@@ -25,13 +25,14 @@ import (
 	structpb "github.com/golang/protobuf/ptypes/struct"
 	schemav1 "github.com/hashicorp/terraform-plugin-sdk/helper/schema"
 	schemav2 "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+
 	"github.com/pulumi/pulumi/sdk/v3/go/common/resource"
 	"github.com/pulumi/pulumi/sdk/v3/go/common/resource/plugin"
 	"github.com/pulumi/pulumi/sdk/v3/go/common/tokens"
 	"github.com/pulumi/pulumi/sdk/v3/go/common/util/contract"
 	pulumirpc "github.com/pulumi/pulumi/sdk/v3/proto/go"
-	"github.com/stretchr/testify/assert"
-	"github.com/stretchr/testify/require"
 
 	"github.com/pulumi/pulumi-terraform-bridge/v3/internal/testprovider"
 	shim "github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tfshim"
@@ -44,7 +45,7 @@ func makeTerraformInputs(olds, news resource.PropertyMap,
 	tfs shim.SchemaMap, ps map[string]*SchemaInfo) (map[string]interface{}, AssetTable, error) {
 
 	ctx := &conversionContext{Assets: AssetTable{}}
-	inputs, err := ctx.MakeTerraformInputs(olds, news, tfs, ps, false)
+	inputs, err := ctx.makeTerraformInputs(olds, news, tfs, ps)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -58,7 +59,7 @@ func makeTerraformInputsWithDefaults(olds, news resource.PropertyMap,
 		Assets:        AssetTable{},
 		ApplyDefaults: true,
 	}
-	inputs, err := ctx.MakeTerraformInputs(olds, news, tfs, ps, false)
+	inputs, err := ctx.makeTerraformInputs(olds, news, tfs, ps)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -67,7 +68,7 @@ func makeTerraformInputsWithDefaults(olds, news resource.PropertyMap,
 
 func makeTerraformInput(v resource.PropertyValue, tfs shim.Schema, ps *SchemaInfo) (interface{}, error) {
 	ctx := &conversionContext{}
-	return ctx.MakeTerraformInput("v", resource.PropertyValue{}, v, tfs, ps, false)
+	return ctx.makeTerraformInput("v", resource.PropertyValue{}, v, tfs, ps, false)
 }
 
 // TestTerraformInputs verifies that we translate Pulumi inputs into Terraform inputs.
@@ -2452,3 +2453,111 @@ func TestOutputNumberTypes(t *testing.T) {
 		"ggg": resource.NewNumberProperty(50),
 	}, outputs)
 }
+
+func TestMakeTerraformInputsOnMapNestedObjects(t *testing.T) {
+	r := &schemav2.Resource{
+		Schema: map[string]*schemav2.Schema{
+			"map_prop": {
+				Type:     schemav2.TypeMap,
+				Optional: true,
+				Elem: &schemav2.Schema{
+					Type:     schemav2.TypeList,
+					Optional: true,
+					Elem: &schemav2.Resource{
+						Schema: map[string]*schemav2.Schema{
+							"x_prop": {
+								Optional: true,
+								Type:     schemav2.TypeString,
+							},
+						},
+					},
+				},
+			},
+		},
+	}
+
+	shimmedR := shimv2.NewResource(r)
+	ctx := context.Background()
+	var instance *PulumiResource
+
+	type testCase struct {
+		name   string
+		ps     map[string]*SchemaInfo
+		config resource.PropertyMap
+		news   resource.PropertyMap
+		olds   resource.PropertyMap
+		expect interface{}
+	}
+
+	testCases := []testCase{
+		{
+			name: "translates x_prop",
+			news: resource.PropertyMap{
+				"mapProp": resource.NewObjectProperty(resource.PropertyMap{
+					"elem1": resource.NewArrayProperty([]resource.PropertyValue{
+						resource.NewObjectProperty(resource.PropertyMap{
+							"xProp": resource.NewStringProperty("xPropValue"),
+						}),
+					}),
+				}),
+			},
+			expect: map[string]interface{}{
+				"__defaults": []interface{}{},
+				"map_prop": map[string]interface{}{
+					"elem1": []interface{}{
+						map[string]interface{}{
+							"__defaults": []interface{}{},
+							"x_prop":     "xPropValue",
+						},
+					},
+				},
+			},
+		},
+		{
+			name: "respects x_prop renames",
+			news: resource.PropertyMap{
+				"mapProp": resource.NewObjectProperty(resource.PropertyMap{
+					"elem1": resource.NewArrayProperty([]resource.PropertyValue{
+						resource.NewObjectProperty(resource.PropertyMap{
+							"x": resource.NewStringProperty("xPropValue"),
+						}),
+					}),
+				}),
+			},
+			ps: map[string]*SchemaInfo{
+				"map_prop": {
+					Elem: &SchemaInfo{
+						Elem: &SchemaInfo{
+							Fields: map[string]*SchemaInfo{
+								"x_prop": {
+									Name: "x",
+								},
+							},
+						},
+					},
+				},
+			},
+			expect: map[string]interface{}{
+				"__defaults": []interface{}{},
+				"map_prop": map[string]interface{}{
+					"elem1": []interface{}{
+						map[string]interface{}{
+							"__defaults": []interface{}{},
+							"x_prop":     "xPropValue",
+						},
+					},
+				},
+			},
+		},
+	}
+
+	for _, tc := range testCases {
+		tc := tc
+
+		t.Run(tc.name, func(t *testing.T) {
+			i, _, err := MakeTerraformInputs(ctx, instance, tc.config, tc.olds, tc.news, shimmedR.Schema(), tc.ps)
+			require.NoError(t, err)
+			require.Equal(t, tc.expect, i)
+		})
+	}
+}
diff --git a/pkg/tfgen/generate_schema_test.go b/pkg/tfgen/generate_schema_test.go
index cd701b38..703803df 100644
--- a/pkg/tfgen/generate_schema_test.go
+++ b/pkg/tfgen/generate_schema_test.go
@@ -57,6 +57,15 @@ func TestRegressMiniRandom(t *testing.T) {
 	bridgetesting.AssertEqualsJSONFile(t, "test_data/regress-minirandom-schema.json", schema)
 }
 
+func TestCSharpMiniRandom(t *testing.T) {
+	provider := testprovider.ProviderMiniRandomCSharp()
+	schema, err := GenerateSchema(provider, diag.DefaultSink(io.Discard, io.Discard, diag.FormatOptions{
+		Color: colors.Never,
+	}))
+	assert.NoError(t, err)
+	bridgetesting.AssertEqualsJSONFile(t, "test_data/minirandom-schema-csharp.json", schema)
+}
+
 func TestNestedMaxItemsOne(t *testing.T) {
 	provider := testprovider.ProviderMiniCloudflare()
 	meta, err := metadata.New(nil)
diff --git a/pkg/tfgen/generate_test.go b/pkg/tfgen/generate_test.go
index d75b2448..14a92993 100644
--- a/pkg/tfgen/generate_test.go
+++ b/pkg/tfgen/generate_test.go
@@ -285,74 +285,73 @@ func Test_ProviderWithObjectTypesInConfigCanGenerateRenames(t *testing.T) {
 	assert.Equal(t, "foo_bar", r.Renames.RenamedProperties["test:index/ProviderProp:ProviderProp"]["fooBar"])
 }
 
-func Test_ProviderWithOmittedTypes(t *testing.T) {
-
-	gen := func(t *testing.T, f func(*tfbridge.ResourceInfo)) pschema.PackageSpec {
-		strType := (&shimschema.Schema{Type: shim.TypeString}).Shim()
-		nestedObj := (&shimschema.Schema{
+func generateNestedSchema(t *testing.T, f func(*tfbridge.ResourceInfo)) pschema.PackageSpec {
+	strType := (&shimschema.Schema{Type: shim.TypeString}).Shim()
+	mkObj := func(m shim.SchemaMap) shim.Schema {
+		return (&shimschema.Schema{
 			Type:     shim.TypeMap,
 			Optional: true,
-			Elem: (&shimschema.Resource{
-				Schema: shimschema.SchemaMap{
-					"fizz_buzz": strType,
-				},
-			}).Shim(),
+			Elem:     (&shimschema.Resource{Schema: m}).Shim(),
 		}).Shim()
-		objType := (&shimschema.Schema{
-			Type:     shim.TypeMap,
-			Optional: true,
-			Elem: (&shimschema.Resource{
+	}
+
+	nestedObj := mkObj(shimschema.SchemaMap{
+		"fizz_buzz": strType,
+	})
+	objType := mkObj(shimschema.SchemaMap{
+		"foo_bar": strType,
+		"nested":  nestedObj,
+	})
+
+	p := (&shimschema.Provider{
+		ResourcesMap: shimschema.ResourceMap{
+			"test_res": (&shimschema.Resource{
 				Schema: shimschema.SchemaMap{
-					"foo_bar": strType,
-					"nested":  nestedObj,
+					"obj": objType,
 				},
 			}).Shim(),
-		}).Shim()
-
-		p := (&shimschema.Provider{
-			ResourcesMap: shimschema.ResourceMap{
-				"test_res": (&shimschema.Resource{
-					Schema: shimschema.SchemaMap{
-						"obj": objType,
-					},
-				}).Shim(),
-			},
-		}).Shim()
+		},
+	}).Shim()
 
-		nilSink := diag.DefaultSink(io.Discard, io.Discard, diag.FormatOptions{
-			Color: colors.Never,
-		})
+	nilSink := diag.DefaultSink(io.Discard, io.Discard, diag.FormatOptions{
+		Color: colors.Never,
+	})
 
-		res := &tfbridge.ResourceInfo{
-			Tok: "test:index:Bar",
-		}
-		if f != nil {
-			f(res)
-		}
+	res := &tfbridge.ResourceInfo{
+		Tok: "test:index:Bar",
+	}
+	if f != nil {
+		f(res)
+	}
 
-		r, err := GenerateSchemaWithOptions(GenerateSchemaOptions{
-			DiagnosticsSink: nilSink,
-			ProviderInfo: tfbridge.ProviderInfo{
-				Name: "test",
-				P:    p,
-				Resources: map[string]*tfbridge.ResourceInfo{
-					"test_res": res,
-				},
+	r, err := GenerateSchemaWithOptions(GenerateSchemaOptions{
+		DiagnosticsSink: nilSink,
+		ProviderInfo: tfbridge.ProviderInfo{
+			Name: "test",
+			P:    p,
+			Resources: map[string]*tfbridge.ResourceInfo{
+				"test_res": res,
 			},
-		})
-		require.NoError(t, err)
-		return r.PackageSpec
-	}
+		},
+	})
+	require.NoError(t, err)
+	return r.PackageSpec
+}
+
+func Test_ProviderWithOmittedTypes(t *testing.T) {
+	t.Parallel()
 
 	t.Run("no-omit", func(t *testing.T) {
-		spec := gen(t, nil)
+		t.Parallel()
+		spec := generateNestedSchema(t, nil)
 		assert.Len(t, spec.Resources, 1)
 		assert.Len(t, spec.Resources["test:index:Bar"].InputProperties, 1)
 		assert.Len(t, spec.Types, 2)
 	})
 
 	t.Run("omit-top-level-prop", func(t *testing.T) {
-		spec := gen(t, func(info *tfbridge.ResourceInfo) {
+		t.Parallel()
+		spec := generateNestedSchema(t, func(info *tfbridge.ResourceInfo) {
 			info.Fields = map[string]*tfbridge.SchemaInfo{
 				"obj": {Omit: true},
 			}
@@ -363,7 +362,8 @@ func Test_ProviderWithOmittedTypes(t *testing.T) {
 	})
 
 	t.Run("omit-nested-prop", func(t *testing.T) {
-		spec := gen(t, func(info *tfbridge.ResourceInfo) {
+		t.Parallel()
+		spec := generateNestedSchema(t, func(info *tfbridge.ResourceInfo) {
 			info.Fields = map[string]*tfbridge.SchemaInfo{
 				"obj": {
 					Elem: &tfbridge.SchemaInfo{
@@ -381,6 +381,195 @@ func Test_ProviderWithOmittedTypes(t *testing.T) {
 
 }
 
+func Test_ProviderWithMovedTypes(t *testing.T) {
+	t.Parallel()
+
+	t.Run("none", func(t *testing.T) {
+		t.Parallel()
+		spec := generateNestedSchema(t, nil)
+		assert.Len(t, spec.Resources, 1)
+		assert.Len(t, spec.Resources["test:index:Bar"].InputProperties, 1)
+		assert.Len(t, spec.Types, 2)
+		assert.Contains(t, spec.Types, "test:index/BarObj:BarObj")
+		assert.Contains(t, spec.Types, "test:index/BarObjNested:BarObjNested")
+	})
+
+	t.Run("top-level", func(t *testing.T) {
+		t.Parallel()
+		spec := generateNestedSchema(t, func(info *tfbridge.ResourceInfo) {
+			info.Fields = map[string]*tfbridge.SchemaInfo{
+				"obj": {
+					Elem: &tfbridge.SchemaInfo{
+						Type: "test:moved:Top",
+					},
+				},
+			}
+		})
+
+		assert.Len(t, spec.Resources, 1)
+		assert.Len(t, spec.Types, 2)
+		if assert.Contains(t, spec.Types, "test:moved:Top") {
+			assert.Contains(t, spec.Types, "test:moved/TopNested:TopNested")
+		}
+	})
+
+	t.Run("nested-prop", func(t *testing.T) {
+		t.Parallel()
+		spec := generateNestedSchema(t, func(info *tfbridge.ResourceInfo) {
+			info.Fields = map[string]*tfbridge.SchemaInfo{
+				"obj": {
+					Elem: &tfbridge.SchemaInfo{
+						Fields: map[string]*tfbridge.SchemaInfo{
+							"nested": {
+								Elem: &tfbridge.SchemaInfo{
+									Type: "test:moved:Nested",
+								},
+							},
+						},
+					},
+				},
+			}
+		})
+		assert.Len(t, spec.Resources, 1)
+		assert.Len(t, spec.Types, 2)
+		assert.Contains(t, spec.Types, "test:index/BarObj:BarObj")
+		assert.Contains(t, spec.Types, "test:moved:Nested")
+	})
+
+	strType := (&shimschema.Schema{Type: shim.TypeString}).Shim()
+	mkObj := func(m shim.SchemaMap) shim.Schema {
+		return (&shimschema.Schema{
+			Type:     shim.TypeMap,
+			Optional: true,
+			Elem:     (&shimschema.Resource{Schema: m}).Shim(),
+		}).Shim()
+	}
+	nilSink := diag.DefaultSink(io.Discard, io.Discard, diag.FormatOptions{
+		Color: colors.Never,
+	})
+
+	t.Run("conflict", func(t *testing.T) {
+		t.Parallel()
+
+		nestedObj := mkObj(shimschema.SchemaMap{
+			"fizz_buzz": strType,
+		})
+		objType := mkObj(shimschema.SchemaMap{
+			"foo_bar": strType,
+			"nested":  nestedObj,
+		})
+
+		p := (&shimschema.Provider{
+			ResourcesMap: shimschema.ResourceMap{
+				"test_res": (&shimschema.Resource{
+					Schema: shimschema.SchemaMap{
+						"obj": objType,
+					},
+				}).Shim(),
+				"test_other": (&shimschema.Resource{
+					Schema: shimschema.SchemaMap{
+						"obj": nestedObj,
+					},
+				}).Shim(),
+			},
+		}).Shim()
+
+		nameObj := map[string]*tfbridge.SchemaInfo{
+			"obj": {
+				Elem: &tfbridge.SchemaInfo{
+					Type: "test:index:Obj",
+				},
+			},
+		}
+
+		// GenerateSchemaWithOptions should return an error when we force distinct
+		// types to have the same type token. Instead, it panics. We capture this
+		// behavior for now, since we want to assert that it doesn't return
+		// garbage.
+		err := "fatal: An assertion has failed: Obj declared twice with different values"
+		assert.PanicsWithValue(t, err, func() {
+			_, _ = GenerateSchemaWithOptions(GenerateSchemaOptions{
+				DiagnosticsSink: nilSink,
+				ProviderInfo: tfbridge.ProviderInfo{
+					Name: "test",
+					P:    p,
+					Resources: map[string]*tfbridge.ResourceInfo{
+						"test_res": {
+							Tok:    "test:index:Res",
+							Fields: nameObj,
+						},
+						"test_other": {
+							Tok:    "test:index:Other",
+							Fields: nameObj,
+						},
+					},
+				},
+			})
+		})
+	})
+
+	t.Run("no-conflict", func(t *testing.T) {
+		t.Parallel()
+
+		nestedObj := mkObj(shimschema.SchemaMap{
+			"fizz_buzz": strType,
+		})
+		objType := mkObj(shimschema.SchemaMap{
+			"foo_bar": strType,
+			"nested":  nestedObj,
+		})
+
+		p := (&shimschema.Provider{
+			ResourcesMap: shimschema.ResourceMap{
+				"test_res": (&shimschema.Resource{
+					Schema: shimschema.SchemaMap{
+						"field1": objType,
+					},
+				}).Shim(),
+				"test_other": (&shimschema.Resource{
+					Schema: shimschema.SchemaMap{
+						"field2": objType,
+					},
+				}).Shim(),
+			},
+		}).Shim()
+
+		nameObj := func(i int) map[string]*tfbridge.SchemaInfo {
+			return map[string]*tfbridge.SchemaInfo{
+				fmt.Sprintf("field%d", i): {
+					Elem: &tfbridge.SchemaInfo{
+						Type: "test:index:Obj",
+					},
+				},
+			}
+		}
+
+		r, err := GenerateSchemaWithOptions(GenerateSchemaOptions{
+			DiagnosticsSink: nilSink,
+			ProviderInfo: tfbridge.ProviderInfo{
+				Name: "test",
+				P:    p,
+				Resources: map[string]*tfbridge.ResourceInfo{
+					"test_res": {
+						Tok:    "test:index:Res",
+						Fields: nameObj(1),
+					},
+					"test_other": {
+						Tok:    "test:index:Other",
+						Fields: nameObj(2),
+					},
+				},
+			},
+		})
+		require.NoError(t, err)
+
+		assert.Len(t, r.PackageSpec.Resources, 2)
+		assert.Len(t, r.PackageSpec.Types, 2,
+			"Since nested types were declared to have the same name, they should only show up once.")
+	})
+
+}
+
 func TestModulePlacementForType(t *testing.T) {
 	t.Parallel()
 
diff --git a/pkg/tfgen/internal/testprovider/minirandom.go b/pkg/tfgen/internal/testprovider/minirandom.go
index 7912e20a..a5549880 100644
--- a/pkg/tfgen/internal/testprovider/minirandom.go
+++ b/pkg/tfgen/internal/testprovider/minirandom.go
@@ -54,3 +54,42 @@ func ProviderMiniRandom() tfbridge.ProviderInfo {
 		},
 	}
 }
+
+func ProviderMiniRandomCSharp() tfbridge.ProviderInfo {
+	randomPkg := "random"
+	randomMod := "index"
+
+	randomMember := func(mod string, mem string) tokens.ModuleMember {
+		return tokens.ModuleMember(randomPkg + ":" + mod + ":" + mem)
+	}
+
+	randomType := func(mod string, typ string) tokens.Type {
+		return tokens.Type(randomMember(mod, typ))
+	}
+
+	randomResource := func(mod string, res string) tokens.Type {
+		fn := string(unicode.ToLower(rune(res[0]))) + res[1:]
+		return randomType(mod+"/"+fn, res)
+	}
+
+	return tfbridge.ProviderInfo{
+		P:           shimv2.NewProvider(testproviderdata.ProviderMiniRandom()),
+		Name:        "random",
+		Description: "A Pulumi package to safely use randomness in Pulumi programs.",
+		Keywords:    []string{"pulumi", "random"},
+		License:     "Apache-2.0",
+		Homepage:    "https://pulumi.io",
+		Repository:  "https://github.com/pulumi/pulumi-random",
+		Resources: map[string]*tfbridge.ResourceInfo{
+			"random_integer": {
+				Tok:        randomResource(randomMod, "RandomInteger"),
+				CSharpName: "CSharpRandomInteger",
+				Fields: map[string]*tfbridge.SchemaInfo{
+					"seed": {
+						CSharpName: "CSharpSeed",
+					},
+				},
+			},
+		},
+	}
+}
diff --git a/pkg/tfgen/test_data/minirandom-schema-csharp.json b/pkg/tfgen/test_data/minirandom-schema-csharp.json
new file mode 100644
index 00000000..5dc2608b
--- /dev/null
+++ b/pkg/tfgen/test_data/minirandom-schema-csharp.json
@@ -0,0 +1,148 @@
+{
+  "name": "random",
+  "description": "A Pulumi package to safely use randomness in Pulumi programs.",
+  "keywords": [
+    "pulumi",
+    "random"
+  ],
+  "homepage": "https://pulumi.io",
+  "license": "Apache-2.0",
+  "attribution": "This Pulumi package is based on the [`random` Terraform Provider](https://github.com/terraform-providers/terraform-provider-random).",
+  "repository": "https://github.com/pulumi/pulumi-random",
+  "meta": {
+    "moduleFormat": "(.*)(?:/[^/]*)"
+  },
+  "language": {
+    "nodejs": {
+      "packageDescription": "A Pulumi package to safely use randomness in Pulumi programs.",
+      "readme": "\u003e This provider is a derived work of the [Terraform Provider](https://github.com/terraform-providers/terraform-provider-random)\n\u003e distributed under [MPL 2.0](https://www.mozilla.org/en-US/MPL/2.0/). If you encounter a bug or missing feature,\n\u003e first check the [`pulumi-random` repo](https://github.com/pulumi/pulumi-random/issues); however, if that doesn't turn up anything,\n\u003e please consult the source [`terraform-provider-random` repo](https://github.com/terraform-providers/terraform-provider-random/issues).",
+      "compatibility": "tfbridge20",
+      "disableUnionOutputTypes": true
+    },
+    "python": {
+      "readme": "\u003e This provider is a derived work of the [Terraform Provider](https://github.com/terraform-providers/terraform-provider-random)\n\u003e distributed under [MPL 2.0](https://www.mozilla.org/en-US/MPL/2.0/). If you encounter a bug or missing feature,\n\u003e first check the [`pulumi-random` repo](https://github.com/pulumi/pulumi-random/issues); however, if that doesn't turn up anything,\n\u003e please consult the source [`terraform-provider-random` repo](https://github.com/terraform-providers/terraform-provider-random/issues).",
+      "compatibility": "tfbridge20",
+      "pyproject": {}
+    }
+  },
+  "config": {},
+  "provider": {
+    "description": "The provider type for the random package. By default, resources use package-wide configuration\nsettings, however an explicit `Provider` instance may be created and passed during resource\nconstruction to achieve fine-grained programmatic control over provider settings. See the\n[documentation](https://www.pulumi.com/docs/reference/programming-model/#providers) for more information.\n"
+  },
+  "resources": {
+    "random:index/randomInteger:RandomInteger": {
+      "properties": {
+        "keepers": {
+          "type": "object",
+          "additionalProperties": {
+            "$ref": "pulumi.json#/Any"
+          },
+          "description": "Arbitrary map of values that, when changed, will trigger recreation of resource. See [the main provider\ndocumentation](../index.html) for more information.\n"
+        },
+        "max": {
+          "type": "integer",
+          "description": "The maximum inclusive value of the range.\n"
+        },
+        "min": {
+          "type": "integer",
+          "description": "The minimum inclusive value of the range.\n"
+        },
+        "result": {
+          "type": "integer",
+          "description": "The random integer result.\n"
+        },
+        "seed": {
+          "type": "string",
+          "description": "A custom seed to always produce the same value.\n",
+          "language": {
+            "csharp": {
+                "name": "CSharpSeed"
+            }
+          }
+        }
+      },
+      "required": [
+        "max",
+        "min",
+        "result"
+      ],
+      "inputProperties": {
+        "keepers": {
+          "type": "object",
+          "additionalProperties": {
+            "$ref": "pulumi.json#/Any"
+          },
+          "description": "Arbitrary map of values that, when changed, will trigger recreation of resource. See [the main provider\ndocumentation](../index.html) for more information.\n",
+          "willReplaceOnChanges": true
+        },
+        "max": {
+          "type": "integer",
+          "description": "The maximum inclusive value of the range.\n",
+          "willReplaceOnChanges": true
+        },
+        "min": {
+          "type": "integer",
+          "description": "The minimum inclusive value of the range.\n",
+          "willReplaceOnChanges": true
+        },
+        "seed": {
+          "type": "string",
+          "description": "A custom seed to always produce the same value.\n",
+          "willReplaceOnChanges": true,
+          "language": {
+            "csharp": {
+                "name": "CSharpSeed"
+            }
+          }
+        }
+      },
+      "requiredInputs": [
+        "max",
+        "min"
+      ],
+      "stateInputs": {
+        "description": "Input properties used for looking up and filtering RandomInteger resources.\n",
+        "properties": {
+          "keepers": {
+            "type": "object",
+            "additionalProperties": {
+              "$ref": "pulumi.json#/Any"
+            },
+            "description": "Arbitrary map of values that, when changed, will trigger recreation of resource. See [the main provider\ndocumentation](../index.html) for more information.\n",
+            "willReplaceOnChanges": true
+          },
+          "max": {
+            "type": "integer",
+            "description": "The maximum inclusive value of the range.\n",
+            "willReplaceOnChanges": true
+          },
+          "min": {
+            "type": "integer",
+            "description": "The minimum inclusive value of the range.\n",
+            "willReplaceOnChanges": true
+          },
+          "result": {
+            "type": "integer",
+            "description": "The random integer result.\n"
+          },
+          "seed": {
+            "type": "string",
+            "description": "A custom seed to always produce the same value.\n",
+            "willReplaceOnChanges": true,
+            "language": {
+              "csharp": {
+                  "name": "CSharpSeed"
+              }
+            }
+          }
+        },
+        "type": "object"
+      },
+      "language": {
+        "csharp": {
+            "name": "CSharpRandomInteger"
+        }
+      }
+    }
+  }
+}
diff --git a/pkg/tfshim/sdk-v2/cty_test.go b/pkg/tfshim/sdk-v2/cty_test.go
index 5bf91ed5..aa352ef9 100644
--- a/pkg/tfshim/sdk-v2/cty_test.go
+++ b/pkg/tfshim/sdk-v2/cty_test.go
@@ -1,8 +1,11 @@
 package sdkv2
 
 import (
+	"math/big"
 	"testing"
 
+	"github.com/stretchr/testify/require"
+
 	"github.com/hashicorp/go-cty/cty"
 	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
 	"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
@@ -600,6 +603,66 @@ func TestMakeResourceRawConfig(t *testing.T) {
 				}),
 			}),
 		},
+		{
+			name: "Regress aws 3094",
+			schema: func() *schema.Resource {
+				securityGroupRuleNestedBlock := &schema.Resource{
+					Schema: map[string]*schema.Schema{
+						"from_port": {
+							Type:     schema.TypeInt,
+							Required: true,
+						},
+						"to_port": {
+							Type:     schema.TypeInt,
+							Required: true,
+						},
+						"cidr_blocks": {
+							Type:     schema.TypeList,
+							Optional: true,
+							Elem: &schema.Schema{
+								Type: schema.TypeString,
+							},
+						},
+					},
+				}
+				ingress := &schema.Schema{
+					Type:       schema.TypeSet,
+					Optional:   true,
+					Computed:   true,
+					ConfigMode: schema.SchemaConfigModeAttr,
+					Elem:       securityGroupRuleNestedBlock,
+					Set: func(i interface{}) int {
+						return 0
+					},
+				}
+				return &schema.Resource{
+					Schema: map[string]*schema.Schema{
+						"arn": {
+							Type:     schema.TypeString,
+							Computed: true,
+						},
+						"ingress": ingress,
+					},
+				}
+			}(),
+			config: map[string]interface{}{
+				"ingress": []interface{}{
+					map[string]interface{}{
+						"to_port":   terraformUnknownVariableValue,
+						"from_port": terraformUnknownVariableValue,
+					},
+				},
+			},
+			expected: cty.ObjectVal(map[string]cty.Value{
+				"ingress": cty.SetVal([]cty.Value{
+					cty.ObjectVal(map[string]cty.Value{
+						"cidr_blocks": cty.NullVal(cty.List(cty.String)),
+						"from_port":   cty.UnknownVal(cty.Number),
+						"to_port":     cty.UnknownVal(cty.Number),
+					}),
+				}),
+			}),
+		},
 	}
 
 	for _, c := range cases {
@@ -639,3 +702,90 @@ func TestMakeResourceRawConfig(t *testing.T) {
 		})
 	}
 }
+
+func TestRecoverCtyValue(t *testing.T) {
+	type testCase struct {
+		name   string
+		dT     cty.Type
+		value  any
+		expect cty.Value
+	}
+
+	cases := []testCase{
+		{"null", cty.EmptyObject, nil, cty.NullVal(cty.EmptyObject)},
+		{
+			"object with mismatched fields",
+			cty.Object(map[string]cty.Type{
+				"x": cty.String,
+				"y": cty.Bool,
+			}),
+			map[string]any{
+				"y": true,
+				"z": "ignored",
+			},
+			cty.ObjectVal(map[string]cty.Value{
+				"x": cty.NullVal(cty.String),
+				"y": cty.BoolVal(true),
+			}),
+		},
+		{
+			"tuple",
+			cty.Tuple([]cty.Type{cty.String, cty.Number}),
+			[]interface{}{"A", 42},
+			cty.TupleVal([]cty.Value{cty.StringVal("A"), cty.NumberIntVal(42)}),
+		},
+		{
+			"empty object",
+			cty.EmptyObject,
+			map[string]interface{}{},
+			cty.EmptyObjectVal,
+		},
+		{
+			"empty tuple",
+			cty.EmptyTuple,
+			[]interface{}{},
+			cty.EmptyTupleVal,
+		},
+		{
+			"empty map",
+			cty.Map(cty.String),
+			map[string]interface{}{},
+			cty.MapValEmpty(cty.String),
+		},
+		{
+			"empty list",
+			cty.List(cty.String),
+			[]any{},
+			cty.ListValEmpty(cty.String),
+		},
+		{
+			"empty set",
+			cty.Set(cty.String),
+			[]interface{}{},
+			cty.SetValEmpty(cty.String),
+		},
+		{"int", cty.Number, int(42), cty.NumberIntVal(42)},
+		{"int64", cty.Number, int64(42), cty.NumberIntVal(42)},
+		{"uint8", cty.Number, uint8(42), cty.NumberIntVal(42)},
+		{"uint16", cty.Number, uint16(42), cty.NumberIntVal(42)},
+		{"uint32", cty.Number, uint32(42), cty.NumberIntVal(42)},
+		{"uint64", cty.Number, uint64(42), cty.NumberIntVal(42)},
+		{"int8", cty.Number, int8(42), cty.NumberIntVal(42)},
+		{"int16", cty.Number, int16(42), cty.NumberIntVal(42)},
+		{"int32", cty.Number, int32(42), cty.NumberIntVal(42)},
+		{"int64", cty.Number, int64(42), cty.NumberIntVal(42)},
+		{"float64", cty.Number, float64(1.42), cty.NumberFloatVal(1.42)},
+		{"float32", cty.Number, float32(1.42), cty.NumberFloatVal(float64(float32(1.42)))},
+		{"big.Float", cty.Number, big.NewFloat(1.42), cty.NumberVal(big.NewFloat(1.42))},
+	}
+
+	for _, tc := range cases {
+		tc := tc
+		t.Run(tc.name, func(t *testing.T) {
+			r, err := recoverCtyValue(tc.dT, tc.value)
+			require.NoError(t, err)
+			require.Truef(t, tc.expect.RawEquals(r), "expected %s to equal %s",
+				r.GoString(), tc.expect.GoString())
+		})
+	}
+}
diff --git a/pkg/tfshim/util/types_test.go b/pkg/tfshim/util/types_test.go
new file mode 100644
index 00000000..9c58b82a
--- /dev/null
+++ b/pkg/tfshim/util/types_test.go
@@ -0,0 +1,69 @@
+// Copyright 2016-2023, Pulumi Corporation.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package util
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+
+	shim "github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tfshim"
+	"github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tfshim/schema"
+)
+
+func TestIsOfTypeMap(t *testing.T) {
+	testCases := []struct {
+		name        string
+		expectIsMap bool
+		schema      shim.Schema
+	}{
+		{
+			"nil", false,
+			nil,
+		},
+		{
+			"int", false,
+			(&schema.Schema{
+				Type: shim.TypeInt,
+			}).Shim(),
+		},
+		{
+			"string_map", true,
+			(&schema.Schema{
+				Type: shim.TypeMap,
+				Elem: (&schema.Schema{
+					Type: shim.TypeString,
+				}).Shim(),
+			}).Shim(),
+		},
+		{
+			"single_nested_block", false,
+			(&schema.Schema{
+				Type: shim.TypeMap,
+				Elem: (&schema.Resource{
+					Schema: &schema.SchemaMap{},
+				}).Shim(),
+			}).Shim(),
+		},
+	}
+
+	for _, tc := range testCases {
+		tc := tc
+		t.Run(tc.name, func(t *testing.T) {
+			actual := IsOfTypeMap(tc.schema)
+			assert.Equal(t, tc.expectIsMap, actual)
+		})
+	}
+}
diff --git a/testing/go.mod b/testing/go.mod
index 074b661e..b821fa31 100644
--- a/testing/go.mod
+++ b/testing/go.mod
@@ -19,7 +19,7 @@ require (
 	github.com/davecgh/go-spew v1.1.1 // indirect
 	github.com/golang/protobuf v1.5.3 // indirect
 	github.com/pmezard/go-difflib v1.0.0 // indirect
-	github.com/pulumi/pulumi/sdk/v3 v3.93.0
+	github.com/pulumi/pulumi/sdk/v3 v3.96.2
 	golang.org/x/net v0.17.0 // indirect
 	golang.org/x/sys v0.13.0 // indirect
 	golang.org/x/text v0.13.0 // indirect
diff --git a/testing/go.sum b/testing/go.sum
index 88bf8de3..020ab579 100644
--- a/testing/go.sum
+++ b/testing/go.sum
@@ -14,8 +14,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
 github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/pulumi/pulumi/sdk/v3 v3.93.0 h1:5InTUxulGuPMXQbnrruIbOEJylRkRWfoDyQIj6dtsYA=
-github.com/pulumi/pulumi/sdk/v3 v3.93.0/go.mod h1:zeqyIODqbb6GrEyhFV6aJET/xBSXSnF7Bw/EjbYZUnU=
+github.com/pulumi/pulumi/sdk/v3 v3.96.2 h1:q5ZKdf+e9JR+a6Eiueg0Ohy6jCQGk9pO2V7xI/qGP3I=
+github.com/pulumi/pulumi/sdk/v3 v3.96.2/go.mod h1:yvD23IIRiqIXuo4kaZNe5zK/uT0nhO99wr6BVEqoi7A=
 github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
 github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
 golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
diff --git a/x/muxer/tests/go.mod b/x/muxer/tests/go.mod
index 9ac18447..48c3068e 100644
--- a/x/muxer/tests/go.mod
+++ b/x/muxer/tests/go.mod
@@ -24,17 +24,19 @@ require (
 	github.com/cyphar/filepath-securejoin v0.2.4 // indirect
 	github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
 	github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
-	github.com/mattn/go-isatty v0.0.18 // indirect
+	github.com/mattn/go-isatty v0.0.19 // indirect
 	github.com/mattn/go-localereader v0.0.1 // indirect
-	github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b // indirect
+	github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
 	github.com/muesli/cancelreader v0.2.2 // indirect
 	github.com/muesli/reflow v0.3.0 // indirect
-	github.com/muesli/termenv v0.15.1 // indirect
-	github.com/pulumi/esc v0.6.0 // indirect
-	golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect
-	golang.org/x/mod v0.13.0 // indirect
-	golang.org/x/sync v0.4.0 // indirect
-	golang.org/x/tools v0.14.0 // indirect
+	github.com/muesli/termenv v0.15.2 // indirect
+	github.com/pgavlin/fx v0.1.6 // indirect
+	github.com/pulumi/appdash v0.0.0-20231130102222-75f619a67231 // indirect
+	github.com/pulumi/esc v0.6.1-0.20231111193429-44b746a5b3b5 // indirect
+	golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa // indirect
+	golang.org/x/mod v0.14.0 // indirect
+	golang.org/x/sync v0.5.0 // indirect
+	golang.org/x/tools v0.15.0 // indirect
 	google.golang.org/genproto/googleapis/rpc v0.0.0-20230706204954-ccb25ca9f130 // indirect
 )
 
@@ -65,7 +67,7 @@ require (
 	github.com/inconshreveable/mousetrap v1.1.0 // indirect
 	github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
 	github.com/kevinburke/ssh_config v1.2.0 // indirect
-	github.com/mattn/go-runewidth v0.0.14 // indirect
+	github.com/mattn/go-runewidth v0.0.15 // indirect
 	github.com/mitchellh/go-ps v1.0.0 // indirect
 	github.com/mitchellh/go-wordwrap v1.0.1 // indirect
 	github.com/natefinch/atomic v1.0.1 // indirect
@@ -76,8 +78,8 @@ require (
 	github.com/pkg/errors v0.9.1 // indirect
 	github.com/pkg/term v1.1.0 // indirect
 	github.com/pmezard/go-difflib v1.0.0 // indirect
-	github.com/pulumi/pulumi/pkg/v3 v3.94.2
-	github.com/pulumi/pulumi/sdk/v3 v3.94.2
+	github.com/pulumi/pulumi/pkg/v3 v3.96.2
+	github.com/pulumi/pulumi/sdk/v3 v3.96.2
 	github.com/rivo/uniseg v0.4.4 // indirect
 	github.com/rogpeppe/go-internal v1.11.0 // indirect
 	github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06 // indirect
@@ -95,14 +97,13 @@ require (
 	github.com/xanzy/ssh-agent v0.3.3 // indirect
 	github.com/zclconf/go-cty v1.13.2 // indirect
 	go.uber.org/atomic v1.9.0 // indirect
-	golang.org/x/crypto v0.14.0 // indirect
-	golang.org/x/net v0.17.0 // indirect
-	golang.org/x/sys v0.13.0 // indirect
-	golang.org/x/term v0.13.0 // indirect
-	golang.org/x/text v0.13.0 // indirect
+	golang.org/x/crypto v0.15.0 // indirect
+	golang.org/x/net v0.18.0 // indirect
+	golang.org/x/sys v0.14.0 // indirect
+	golang.org/x/term v0.14.0 // indirect
+	golang.org/x/text v0.14.0 // indirect
 	google.golang.org/grpc v1.57.1 // indirect
 	gopkg.in/warnings.v0 v0.1.2 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
 	lukechampine.com/frand v1.4.2 // indirect
-	sourcegraph.com/sourcegraph/appdash v0.0.0-20211028080628-e2786a622600 // indirect
 )
diff --git a/x/muxer/tests/go.sum b/x/muxer/tests/go.sum
index 0c260c12..c68a5162 100644
--- a/x/muxer/tests/go.sum
+++ b/x/muxer/tests/go.sum
@@ -102,26 +102,26 @@ github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVc
 github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=
 github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
 github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
-github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98=
-github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
+github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
+github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
 github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
 github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
 github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
 github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
-github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
-github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
+github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
+github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
 github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc=
 github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg=
 github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=
 github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=
-github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b h1:1XF24mVaiu7u+CFywTdcDo2ie1pzzhwjt6RHqzpMU34=
-github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b/go.mod h1:fQuZ0gauxyBcmsdE3ZT4NasjaRdxmbCS0jRHsrWu3Ho=
+github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI=
+github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo=
 github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
 github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
 github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
 github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
-github.com/muesli/termenv v0.15.1 h1:UzuTb/+hhlBugQz28rpzey4ZuKcZ03MeKsoG7IJZIxs=
-github.com/muesli/termenv v0.15.1/go.mod h1:HeAQPTzpfs016yGtA4g00CsdYnVLJvxsS4ANqrZs2sQ=
+github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo=
+github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8=
 github.com/natefinch/atomic v1.0.1 h1:ZPYKxkqQOx3KZ+RsbnP/YsgvxWQPGxjC0oBt2AhwV0A=
 github.com/natefinch/atomic v1.0.1/go.mod h1:N/D/ELrljoqDyT3rZrsUmtsuzvHkeB/wWjHV22AZRbM=
 github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI=
@@ -130,6 +130,8 @@ github.com/opentracing/basictracer-go v1.1.0/go.mod h1:V2HZueSJEp879yv285Aap1BS6
 github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
 github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs=
 github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
+github.com/pgavlin/fx v0.1.6 h1:r9jEg69DhNoCd3Xh0+5mIbdbS3PqWrVWujkY76MFRTU=
+github.com/pgavlin/fx v0.1.6/go.mod h1:KWZJ6fqBBSh8GxHYqwYCf3rYE7Gp2p0N8tJp8xv9u9M=
 github.com/pgavlin/goldmark v1.1.33-0.20200616210433-b5eb04559386 h1:LoCV5cscNVWyK5ChN/uCoIFJz8jZD63VQiGJIRgr6uo=
 github.com/pgavlin/goldmark v1.1.33-0.20200616210433-b5eb04559386/go.mod h1:MRxHTJrf9FhdfNQ8Hdeh9gmHevC9RJE/fu8M3JIGjoE=
 github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4=
@@ -140,12 +142,14 @@ github.com/pkg/term v1.1.0 h1:xIAAdCMh3QIAy+5FrE8Ad8XoDhEU4ufwbaSozViP9kk=
 github.com/pkg/term v1.1.0/go.mod h1:E25nymQcrSllhX42Ok8MRm1+hyBdHY0dCeiKZ9jpNGw=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/pulumi/esc v0.6.0 h1:m8jWgBektlj26RvrN3/sM0r1lYiwTMmqVPvLxCxahGE=
-github.com/pulumi/esc v0.6.0/go.mod h1:Y6W21yUukvxS2NnS5ae1beMSPhMvj0xNAYcDqDHVj/g=
-github.com/pulumi/pulumi/pkg/v3 v3.94.2 h1:b+gQ+EJZMYfSIu/sb/+p36VmfZ2ZJWbEWMsptt2f6Kk=
-github.com/pulumi/pulumi/pkg/v3 v3.94.2/go.mod h1:aCeDMlncyDwX3RG6+vYUjWsy6bOEtoHUY0twCcn5qqE=
-github.com/pulumi/pulumi/sdk/v3 v3.94.2 h1:B8W3CCzA2fX/0730o/7KQXdmqrY5KdgGtiAA6rDMVlk=
-github.com/pulumi/pulumi/sdk/v3 v3.94.2/go.mod h1:zeqyIODqbb6GrEyhFV6aJET/xBSXSnF7Bw/EjbYZUnU=
+github.com/pulumi/appdash v0.0.0-20231130102222-75f619a67231 h1:vkHw5I/plNdTr435cARxCW6q9gc0S/Yxz7Mkd38pOb0=
+github.com/pulumi/appdash v0.0.0-20231130102222-75f619a67231/go.mod h1:murToZ2N9hNJzewjHBgfFdXhZKjY3z5cYC1VXk+lbFE=
+github.com/pulumi/esc v0.6.1-0.20231111193429-44b746a5b3b5 h1:1DJMji9F7XPea46bSuhy4/85n8J4/Mfz8PWLZtjKKiI=
+github.com/pulumi/esc v0.6.1-0.20231111193429-44b746a5b3b5/go.mod h1:Y6W21yUukvxS2NnS5ae1beMSPhMvj0xNAYcDqDHVj/g=
+github.com/pulumi/pulumi/pkg/v3 v3.96.2 h1:d+zDsh4K1j90q/upDRkhgxHkspP20Xxd3g6OaMbw574=
+github.com/pulumi/pulumi/pkg/v3 v3.96.2/go.mod h1:lR+q06XrnEqRzJXZ6IeHb4nu+U5fswFcc51Q0MRBTio=
+github.com/pulumi/pulumi/sdk/v3 v3.96.2 h1:q5ZKdf+e9JR+a6Eiueg0Ohy6jCQGk9pO2V7xI/qGP3I=
+github.com/pulumi/pulumi/sdk/v3 v3.96.2/go.mod h1:yvD23IIRiqIXuo4kaZNe5zK/uT0nhO99wr6BVEqoi7A=
 github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
 github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
 github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
@@ -203,18 +207,18 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y
 golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
 golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
 golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
-golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
-golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
-golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
-golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
+golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA=
+golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g=
+golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ=
+golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE=
 golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
 golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
 golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
 golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
-golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY=
-golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
+golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
+golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
 golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@@ -226,15 +230,15 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug
 golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
 golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
 golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
-golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
-golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
+golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg=
+golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ=
-golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
+golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
+golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -256,15 +260,15 @@ golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
-golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q=
+golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
 golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
 golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
-golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek=
-golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
+golang.org/x/term v0.14.0 h1:LGK9IlZ8T9jvdy6cTdfKUCltatMFOehAQo9SRC46UQ8=
+golang.org/x/term v0.14.0/go.mod h1:TySc+nGkYR6qt8km8wUhuFRTVSMIX3XPR58y2lC8vww=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
@@ -272,8 +276,8 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
 golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
 golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
 golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
-golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
-golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
+golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
+golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
@@ -282,8 +286,8 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY
 golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
 golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
 golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
-golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc=
-golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg=
+golang.org/x/tools v0.15.0 h1:zdAyfUGbYmuVokhzVmghFl2ZJh5QhcfebBgmVPFYA+8=
+golang.org/x/tools v0.15.0/go.mod h1:hpksKq4dtpQWS1uQ61JkdqWM3LscIS6Slf+VVkm+wQk=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -310,5 +314,3 @@ gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 lukechampine.com/frand v1.4.2 h1:RzFIpOvkMXuPMBb9maa4ND4wjBn71E1Jpf8BzJHMaVw=
 lukechampine.com/frand v1.4.2/go.mod h1:4S/TM2ZgrKejMcKMbeLjISpJMO+/eZ1zu3vYX9dtj3s=
 pgregory.net/rapid v0.6.1 h1:4eyrDxyht86tT4Ztm+kvlyNBLIk071gR+ZQdhphc9dQ=
-sourcegraph.com/sourcegraph/appdash v0.0.0-20211028080628-e2786a622600 h1:hfyJ5ku9yFtLVOiSxa3IN+dx5eBQT9mPmKFypAmg8XM=
-sourcegraph.com/sourcegraph/appdash v0.0.0-20211028080628-e2786a622600/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU=