You can accelerate .NET application modernization with AWS Transform for full-stack Windows modernization—but there may be some follow on work. End-to-end modernization often involves post-transformation tasks to finalize your application for production readiness. By finalize, I mean validating, debugging, and completing your modernized application so that it is ready for use. It’s the human reviewer’s responsibility to check the application for correctness, and combining AWS Transform with an AI code companion to quickly address remaining issues is a common pattern. In your modernization journey, think of AWS Transform as the express travel that takes you close to your destination, and an AI code companion as the local travel for those last few miles.
In this post, I’ll complete the walkthrough I began in a prior post where I transformed a complex blockchain cryptocurrency suite from .NET Framework to modern .NET with AWS Transform. I’ll use the Kiro AI code companion to finalize the transformed application.
The transformed solution builds, but is it correct?
At the end of my previous transformation, I used AWS Transform to modernize a 16-project CryptoCoin suite from .NET Framework 4.8 to .NET 10. The resulting .NET 10 solution builds successfully. However, a solution that builds is not necessarily a solution that works. I need to validate that the transformed application looks and behaves like the original. It’s best to plan how you will validate your modernized application in advance before you transform it.
Have a validation plan for your application
When I validate a transformed application, I look at 4 areas:
- Next Steps: Did AWS Transform give me any Next Steps tasks to address?
- Unit tests: If the solution has unit tests, are they passing?
- Smoke test: Does the app launch and is it nominally working?
- Full validation: Does the application work in full? Can all user tasks be achieved? Does every area of the UI appear correct visually, interact like it should, and exhibit the correct functionality? Is security intact? Are business rules enforced? Is data persisted correctly?
An AI code companion is an essential tool for each of these steps. I’m using Kiro in this walkthrough, but you can use any AI code companion you prefer.
Use an AI code companion to address issues found during validation
Giving Kiro context
I launch Kiro and open the folder where the transformed solution resides. Then, I give Kiro context with the following prompt:
This solution was recently transformed from .NET Framework to .NET 10.
You are going to help me fix issues as I test the modernized application.
It’s sometimes helpful to give Kiro even more context, although it wasn’t necessary in this case. You can tell Kiro to review a folder with the original code for comparison with the transformed code, and you can have it review the transformation report which documents what was changed during the transformation. If you find Kiro struggling with making effective fixes, try giving it this additional context.
1. Next Steps
AWS Transform provided me with a Next Steps markdown file containing post-transformation tasks (shown in Listing 1). Those tasks include required code changes, recommendations, and Linux readiness considerations. It’s a good idea to start here and take care of those tasks before you start validating. Give this to your AI code companion to handle.
Address the Next Steps tasks with an AI Code Companion
# Next Steps
## Transformation Context
- **Solution**: CryptoCoin.sln
- **Source Framework**: .NET Framework 4.8 (VB.NET) / .NET Framework 4.7.2 (C# Web Forms)
- **Target Framework**: net10.0 (applications/tests), netstandard2.0 (class libraries)
- **Projects Transformed**: 16 (15 VB.NET + 1 C#)
- **Transformation Date**: 2025-07-14
### Project Target Framework Summary
| Project | Type | Target Framework |
|---------|------|-----------------|
| CryptoCoin.Cryptography | Class Library | netstandard2.0 |
| CryptoCoin.Core | Class Library | netstandard2.0 |
| CryptoCoin.Transactions | Class Library | netstandard2.0 |
| CryptoCoin.Networking | Class Library | netstandard2.0 |
| CryptoCoin.Mining | Class Library | netstandard2.0 |
| CryptoCoin.Wallet | Class Library | netstandard2.0 |
| CryptoCoin.Sdk | Class Library | netstandard2.0 |
| CryptoCoin.Contracts | Class Library | netstandard2.0 |
| CryptoCoin.Explorer | Class Library | netstandard2.0 |
| CryptoCoin.Persistence | Class Library | netstandard2.0 |
| CryptoCoin.Services | Class Library | netstandard2.0 |
| CryptoCoin.Node | Console App (Exe) | net10.0 |
| CryptoCoin.Demo | Console App (Exe) | net10.0 |
| CryptoCoin.WalletCli | Console App (Exe) | net10.0 |
| CryptoCoin.Web.BlockExplorer | Web App | net10.0 |
| CryptoCoin.Tests | Test Project | net10.0 |
## Current Build Status
The solution builds successfully with **0 errors**. All 16 projects compile cleanly after the transformation.
## Remaining Build Errors
None. The build is clean.
## Incomplete Transformations
No projects were left in a partially transformed state. All 16 projects were fully converted. However, several areas require post-transformation review and refinement:
### CryptoCoin.Explorer (netstandard2.0 with OutputType Exe)
- `src/CryptoCoin.Explorer/CryptoCoin.Explorer.vbproj` has `<OutputType>Exe</OutputType>` while targeting `netstandard2.0`. This is technically invalid — netstandard2.0 cannot produce an executable. The project compiles because it has a `Program.vb` but the resulting assembly may not be directly runnable. Consider changing the target framework to `net10.0` or removing the `OutputType` element if this project is only consumed as a library by `CryptoCoin.Node`.
### CryptoCoin.Services (netstandard2.0 with ASP.NET Core 2.2 packages)
- `src/CryptoCoin.Services/CryptoCoin.Services.vbproj` references `Microsoft.AspNetCore.Mvc.Core 2.2.5`, `Microsoft.AspNetCore.Http.Abstractions 2.2.0`, and `Swashbuckle.AspNetCore 6.5.0` while targeting netstandard2.0. This works at build time but may cause runtime conflicts when consumed by the net10.0 host (`CryptoCoin.Node`). Consider changing the target to `net10.0` and using `<FrameworkReference Include="Microsoft.AspNetCore.App" />` instead of individual ASP.NET Core 2.2 packages.
## Package Issues
| Package (Original) | Replacement | Status | Notes |
|---------------------|-------------|--------|-------|
| Castle.Windsor 5.1.2 | Microsoft.Extensions.DependencyInjection 9.0.6 | ✅ Complete | `NodeContainer.vb` uses `ServiceCollection`/`ServiceProvider` |
| log4net 2.0.15 | Microsoft.Extensions.Logging.Console 9.0.6 | ✅ Complete | `NodeLogger.vb` uses `ILoggerFactory` |
| EnterpriseLibrary.Logging 6.0.1304 | Microsoft.Extensions.Logging.Console 9.0.6 | ✅ Complete | Merged into single logging implementation |
| System.Data.SQLite.Core 1.0.118.0 | Microsoft.Data.Sqlite 8.0.11 | ✅ Complete | `SqliteBlockStore.vb` uses `Microsoft.Data.Sqlite` |
| Newtonsoft.Json 13.0.3 | Newtonsoft.Json 13.0.4 | ✅ Updated | Retained (not replaced with System.Text.Json) |
| jQuery 3.7.0 (NuGet) | jQuery 3.7.0 (NuGet) | ⚠️ Review | jQuery NuGet package reference in the csproj is unusual for modern ASP.NET Core; static files are typically managed via `wwwroot` directly or a CDN/libman |
### Recommendations
1. **Newtonsoft.Json → System.Text.Json**: The solution uses Newtonsoft.Json in `CryptoCoin.Core`, `CryptoCoin.Sdk`, and `CryptoCoin.Web.BlockExplorer`. Consider migrating to `System.Text.Json` (built into .NET 10) to reduce external dependencies. This is optional — Newtonsoft.Json works fine on .NET 10.
2. **jQuery NuGet package**: Remove the `<PackageReference Include="jQuery" ... />` from `CryptoCoin.Web.BlockExplorer.csproj`. The jQuery files already exist in `wwwroot/Scripts/`. Use LibraryManager (`libman.json`) or a CDN reference instead if updates are needed.
3. **Microsoft.AspNetCore packages in Services project**: Replace explicit `Microsoft.AspNetCore.Mvc.Core 2.2.5` and `Microsoft.AspNetCore.Http.Abstractions 2.2.0` references with a `FrameworkReference` if the project is retargeted to net10.0.
## Code Changes Required
### 1. Fix CryptoCoin.Explorer target framework mismatch
**File**: `src/CryptoCoin.Explorer/CryptoCoin.Explorer.vbproj`
**Action**: Either remove `<OutputType>Exe</OutputType>` (if this is consumed only as a library) or change `<TargetFramework>` to `net10.0`.
### 2. Upgrade CryptoCoin.Services to use FrameworkReference
**File**: `src/CryptoCoin.Services/CryptoCoin.Services.vbproj`
**Action**: Change target to `net10.0` and replace individual ASP.NET Core 2.2 package references:
```xml
<!-- Remove these -->
<PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version="2.2.5" />
<PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Http" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="2.2.0" />
<!-- Add this -->
<FrameworkReference Include="Microsoft.AspNetCore.App" />
```
### 3. Set API key via user secrets or environment variable
**File**: `src/CryptoCoin.Web.BlockExplorer/appsettings.json`
**Action**: The `ApiKey` value is `<<SECRET>>`. For local development, use `dotnet user-secrets set "ApiEndpoints:ApiKey" "cryptocoin-demo-key"`. For production, use an environment variable: `ApiEndpoints__ApiKey`.
### 4. Review WCF client URL paths for correctness
**File**: `src/CryptoCoin.Web.BlockExplorer/WcfBlockchainClient.cs`
**Action**: The `BlockchainServiceProxy` class constructs URLs like `{_baseUrl}/GetBlockCount`. Verify these match the actual route patterns exposed by the controllers in `CryptoCoin.Services`. The `NodeServiceHost` suggests endpoints are at `/api/blockchain` and `/api/wallet`, meaning the full URLs would be something like `http://localhost:8090/api/blockchain/GetBlockCount`. Adjust the proxy URL construction or the `appsettings.json` base URL accordingly.
### 5. Review WcfWalletClient.cs for similar URL pattern issues
**File**: `src/CryptoCoin.Web.BlockExplorer/WcfWalletClient.cs`
**Action**: Same as above — verify the HTTP client URL patterns match the actual controller routes.
### 6. Remove stale `_PORT_TODO_` comments from appsettings.json
**File**: `src/CryptoCoin.Web.BlockExplorer/appsettings.json`
**Action**: Remove the `_PORT_TODO_*` keys — they are transformation notes, not configuration:
- `_PORT_TODO_secrets`
- `_PORT_TODO_systemWeb`
- `_PORT_TODO_assemblyBindings`
- `_PORT_TODO_systemCodedom`
### 7. Update README.md
**File**: `README.md`
**Action**: The README still describes the project as ".NET Framework 4.8" and references MSBuild command lines, `Web.config`, WCF services, and ASMX. Update it to reflect the new .NET 10 target, `dotnet build`/`dotnet run` commands, `appsettings.json` configuration, and the ASP.NET Core Web API + Razor Pages architecture.
### 8. Consider adding appsettings.Development.json
**File**: `src/CryptoCoin.Web.BlockExplorer/appsettings.Development.json` (new)
**Action**: Create this file with the development API key and explorer URL to simplify local development:
```json
{
"ApiEndpoints": {
"ApiKey": "cryptocoin-demo-key"
},
"ExplorerBaseUrl": "http://localhost:8080"
}
```
## Validation & Testing
### Build Verification
```bash
dotnet build CryptoCoin.sln --configuration Release
```
### Run Unit Tests
```bash
dotnet test src/CryptoCoin.Tests/CryptoCoin.Tests.vbproj --configuration Release --verbosity normal
```
### Run Individual Applications
```bash
# Demo app (quick smoke test)
dotnet run --project src/CryptoCoin.Demo/CryptoCoin.Demo.vbproj
# Full node with regtest
dotnet run --project src/CryptoCoin.Node/CryptoCoin.Node.vbproj -- --regtest --mine CJTZijYXJ4n3XisgX2jVioSWtcThfL31PC --explorer 8080 --service 8090
# Wallet CLI
dotnet run --project src/CryptoCoin.WalletCli/CryptoCoin.WalletCli.vbproj
# Web Block Explorer
dotnet run --project src/CryptoCoin.Web.BlockExplorer/CryptoCoin.Web.BlockExplorer.csproj
```
### Key Areas to Test Manually
1. **CryptoCoin.Demo**: Run and verify all cryptographic operations produce expected output (mnemonic generation, key derivation, signing, Merkle tree, Base58).
2. **CryptoCoin.Node**: Start in regtest mode with mining enabled. Verify blocks are mined and the RPC server responds to `getblockcount`.
3. **CryptoCoin.Node + Persistence**: Start with `--persist` and verify SQLite database is created in the data directory. Restart the node and confirm chain height is preserved.
4. **Explorer API**: With the node running (`--explorer 8080`), verify `http://localhost:8080/api/status` returns JSON.
5. **Service API**: With the node running (`--service 8090`), verify `http://localhost:8090/api/blockchain/GetBlockCount` returns a value (include `X-Api-Key` header).
6. **Web Block Explorer**: Start both the node and the web project. Navigate to the dashboard and verify it displays blocks. Test the global search bar and wallet pages.
7. **Unit Tests**: Run the full test suite and verify all tests pass. Pay special attention to:
- `CryptoCoin.Tests/Services/BlockchainServiceTests.vb` — validates the transformed service layer
- `CryptoCoin.Tests/Services/ApiKeyHeaderTests.vb` — validates the new middleware-based auth
- `CryptoCoin.Tests/Wallet/KeyStoreTests.vb` — validates AES encryption still works
### Behavioral Changes to Watch For
1. **Logging output**: Previously logged to files (`node.log`, `node-entlib.log`). Now logs to console only. If file logging is required, add `Microsoft.Extensions.Logging` file-based providers (e.g., `Serilog.Extensions.Logging.File`).
2. **DI container lifetime**: Castle.Windsor had different lifetime semantics. All services are registered as Singleton in the new `NodeContainerFactory`. Verify no services expect transient/scoped behavior.
3. **SQLite API differences**: `Microsoft.Data.Sqlite` has slightly different behavior from `System.Data.SQLite` — notably around type affinity and BLOB handling. The persistence tests should catch any issues.
4. **WCF → Web API**: The service endpoint URLs have changed from SOAP/BasicHttpBinding to REST/JSON. Any external consumers of the old WCF services will need to be updated.
5. **Web Forms → Razor Pages**: URL routing changed (e.g., `/Block.aspx?hash=...` → `/Explorer/Block?hash=...`). The `Pages/` folder structure determines routes.
6. **ASMX → Minimal API**: `PriceService.asmx` is now minimal API endpoints at `/api/price/current`, `/api/price/history`, `/api/price/stats`, `/api/price/convert`. Update any client-side JavaScript that called the ASMX endpoint.
7. **Command-line flag change**: The `--wcf` flag was renamed to `--service` and `--wcfkey` to `--servicekey` in the node application.
## Deployment Considerations
### Runtime Requirements
- .NET 10 SDK/Runtime required on build and deployment machines.
- No IIS dependency — all applications are self-hosted (Kestrel for web, console hosts for services).
- SQLite native binaries are bundled by the `Microsoft.Data.Sqlite` package (no manual `lib/SQLite/` folder needed).
### Configuration Changes
- **Web.config is removed**. Configuration is now in `appsettings.json` for the web project and command-line arguments for the node.
- **Sensitive values** (API keys): Use environment variables (`ApiEndpoints__ApiKey`) or .NET User Secrets for development. Do not commit real keys to `appsettings.json`.
### Platform Considerations
- The solution is now cross-platform (Windows, Linux, macOS) since it targets .NET 10.
- The `lib/SQLite/` directory with native x86/x64 binaries is no longer needed — `Microsoft.Data.Sqlite` handles native dependency resolution.
- If deploying to Linux, verify all file paths in the codebase use `Path.Combine` rather than hardcoded backslashes.
### Port Configuration
- **RPC Server**: Port 8332 (default, configurable via `--rpcport`)
- **Explorer API**: Configurable via `--explorer <port>` (e.g., 8080)
- **Service API**: Configurable via `--service <port>` (e.g., 8090)
- **Web Block Explorer**: Default Kestrel ports (5000 HTTP / 5001 HTTPS) or configured via `ASPNETCORE_URLS`
Listing 1: Next Steps markdown
I tell Kiro about the Next Steps markdown file and ask it to address the tasks (Figure 1).
The document NextSteps.md contains recommended next steps (remaining modernization tasks) from AWS Transform. Review the document and propose updates to address these tasks.
Figure 1: Kiro working on Next Steps tasks
Kiro gets to work, reviewing Next Steps and planning tasks. One of those tasks is a big one: the wallet service contains stubs and wasn’t fully implemented. After a few minutes, Kiro says it has addressed all of the tasks. Did it do a good job? We’ll find out as we validate the application. Figure 2 shows Kiro’s summary of changes it made. The WalletService was fully implemented, and wired up to the Wallet Manager library. The web UI search form was implemented. The solution was checked for correctness of settings and API key. Files that are no longer needed were removed.
Figure 2: Kiro summary of Next Steps tasks completed
2. Unit Tests
Not all applications have unit tests, but when you have them you should use them. AWS Transform will port any existing unit tests projects in the solution during transformation and run them for you, with results in the transformation report.
Validate unit tests, if present
The original CryptoCoin had 326 unit tests, which AWS Transform ported to .NET 10 along with the rest of the solution code. When I run Test Explorer, 322 of those test pass and 4 fail (Figure 3).
Figure 3: Some unit tests failing after transformation
Although the majority of unit tests are passing, I of course need all of them to pass. I copy the unit test failure information from Test Explorer to Kiro and ask it to fix the tests (Figure 4).
When I run the unit tests project, 322 tests pass but 4 fail. Help me fix them.
<error details>
Figure 4: Putting Kiro to work on failed unit tests
Kiro studies this information, makes code changes, and reports that it has corrected the tests. It indicates it addressed some arithmetic overflow issues and some missing types. When I rebuild and rerun the unit tests, all of them pass (Figure 5).
Figure 5: All unit tests passing
Unit test validation is complete. We don’t yet have confirmation that the application works as a whole, but we’ve confirmed all of the unit tests are passing on the transformed code, which builds confidence.
3. Smoke Test
The next validation step is a smoke test to confirm whether basic functionality is working.
Run a smoke test to confirm app can launch with basic functionality
The CryptoCoin suite includes a console app named CryptoCoin.Demo that calls the various class libraries to exercise its core functionality. While this doesn’t include the web front end and the web services, it does serve to confirm whether the components at the core of CryptoCoin are working together correctly.
I launch the demo project, but it fails midway through with a runtime error (Figure 6), a System.OverFlowException exception.
Figure 6: Demo console project runtime error
I give Kiro the exception details and instruct it to resolve the runtime error (Figure 7). It identifies and resolves the issue, again related to arithmetic overflow.
Help me fix a runtime error. The project CryptoCoinDemo is a console program that invokes the class libraries. When I run it, the output gets up to “<output> and then fails with this runtime error: <exception detail>
Figure 7: Putting Kiro to work fixing a runtime error
The analysis (Figure 8) reveals an interesting discovery: the original CryptoCoin library depended on a .NET Framework overflow behavior that is different in modern .NET: .NET Framework unsigned integer arithmetic was implicitly unchecked, whereas modern .NET checks arithmetic by default. I already bumped into this in the prior section when we were fixing unit tests, but now I learn that this is a more fundamental issue. Fortunately, Kiro knows what to do. It adds an arithmetic helper class to provide the behavior the code expects.
Figure 8: Kiro analysis of runtime error and fix
After rebuilding the solution and rerunning the demo project, it now completes without error (Figure 9). My smoke test is now running, and the core functionality of CryptoCoin is confirmed.
Figure 9: CryptoCoin demo project running
4. End-to-end Validation
The final validation step is the most work: does the application fully operate with fidelity to the original? Do all its parts look and function as they should, including front-end UI and back-end services or APIs? Can users successfully perform business tasks end-to-end? Is security intact? Is data persisted correctly?
Validate application UI and functionality end-to-end
For CryptoCoin, that means ensuring the app launches and then trying out everything it does. We run its back-end, a project named CryptoCoin.Node, and front-end, a project named CryptoCoin.Web.BlockExplorer. Figure 10 shows the back end and front end after they startup. The back end Node project, shown on the left, runs without error and the console output confirms its hosted services are running. It also begins blockchain mining.
However, the front end website shown on the right doesn’t load at all and has a 404 error. I’m not completely surprised, because AWS Transform did warn me earlier that the website transformation from Web Forms to Razer pages was complex and would require some attention.
Figure 10: Attempt to run back end and front end together
I tell Kiro the website is not loading and it finds the issue. Index.cshtml and other files are missing. It fixes this and when I rerun the code, the website homepage loads (Figure 11). It isn’t styled correctly, but it is functional. I can see that data is retrieved, and links work when I try them.
Figure 11: Website loads and is functional but styling is missing
I tell Kiro the styling is missing and show it what I mean with a screenshot. Kiro corrects the location of the CSS files. Now when I run the website it is much closer to the original (Figure 12).
Figure 12: Website mostly styled correctly but missing
But it’s not perfect. The banner image is missing and navigation isn’t correct. Kiro corrects the site master layout and things are looking better. I test out different functions on the website and it seems to be working well so far (Figure 13).
Figure 13: Website styling restored
I need to be thorough and exhaustive. When I try out the wallet pages, the create wallet function does nothing when I submit the form (Figure 14). There’s some error information on the back end indicating the wallet service could not be reached. I tell Kiro.
When I try the Create Wallet page in the web project, I get this error: Could not create wallet. Wallet service is unavailable. Make sure the node is running. This is the output from the Node project <output>
Figure 14: Website Create Wallet function not working
Kiro finds and corrects some client and service address and port mismatches. Now the wallet functions are working (Figure 15). All of it is working. Validation is now complete and Crypto Coin has been fully modernized.
Figure 15: Create Wallet function working after Kiro fixes
Conclusion
In this post, I completed a complex modernization with Kiro that I started with AWS Transform. Kiro was an essential partner through each stage of validation. After giving Kiro context on the transformed solution, it handled the Next Steps tasks from AWS Transform, fixed broken unit tests, and debugged a runtime error, in each case with just a single prompt. It also fixed web site styling, navigation, and service connectivity issues, where I gave it a series of prompts as I progressively discovered issues. Kiro not only fixed the issues I asked it to, but it also went above and beyond and provided insightful analysis about arithmetic overflow differences between .NET Framework and .NET, and altered the code to function as originally intended.
AWS Transform and Kiro are a powerful combination for end-to-end .NET application modernization. Get started today with AWS Transform and Kiro.