Dice the carrots into 1/2-inch pieces, chop the celery ribs, dice the onion, and cut the peeled potatoes into 1-inch chunks. Cube the ham into 1/2-inch bite-sized pieces. Having everything prepped and ready will make the cooking process smooth and prevent overcooking any component while you're chopping.
Heat the olive oil in a large pot over medium heat. Add the diced carrots, celery, and onion, sautéing for 5 minutes until they soften slightly and become fragrant. This foundation builds flavor depth into the entire soup—don't skip this step or rush it.
Add the potato chunks, vegetable broth, and cubed ham to the pot with the sautéed vegetables. Bring to a boil, then reduce heat and simmer for 15 minutes until the potatoes are fork-tender. While this simmers, you'll prepare the roux in the next step, so timing works perfectly in parallel.
While the soup simmers, melt the butter in a separate small pan over medium heat. Whisk in the flour and cook for 1 minute to remove the raw flour taste. Gradually whisk in the room-temperature milk (room temperature prevents lumps from forming) and cook for about 4 minutes, stirring frequently, until the mixture thickens into a smooth cream sauce. I like to use warm milk rather than cold because it incorporates more smoothly into the roux.
When the potatoes are tender, stir the pepper and garlic powder into the soup. Pour the roux mixture from Step 4 into the pot while stirring constantly to incorporate it smoothly without lumps. Continue cooking for about 10 minutes over medium-low heat, stirring occasionally, until the soup reaches your desired thickness and flavors meld together.
Taste the soup and add salt to your preference, starting with a small pinch since the broth and ham already provide saltiness. Stir in the fresh parsley just before serving for bright color and fresh flavor. Serve the soup hot and enjoy—this recipe yields enough for a hearty main course or multiple servings.