Note that here is a shorter, maybe a bit cleaner version of this (that I quite enjoy):

$data = Import-Csv .\test.csv

$serialData = [System.Management.Automation.PSSerializer]::Serialize($data)

$data2 = [System.Management.Automation.PSSerializer]::Deserialize($serialData)

Note: However, weirdly, it does not keep the ordering of ordered hashtables.

$data = [ordered] @{
    1 = 1
    2 = 2
}

$serialData = [System.Management.Automation.PSSerializer]::Serialize($data)

$data2 = [System.Management.Automation.PSSerializer]::Deserialize($serialData)
$data2

Will output:

Name                           Value
----                           -----
2                              2
1                              1

While with other types it works just fine:

$data = [PsCustomObject] @{
    1 = 1
    2 = 2
}

$data = @(1, 2, 3)

For getting really deep copies we can use binary serialization (assuming that all data are serializable; this is definitely the case for data that come from CSV):

# Get original data
$data = Import-Csv ...

# Serialize and Deserialize data using BinaryFormatter
$ms = New-Object System.IO.MemoryStream
$bf = New-Object System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
$bf.Serialize($ms, $data)
$ms.Position = 0
$data2 = $bf.Deserialize($ms)
$ms.Close()

# Use deep copied data
$data2

Here's an even shorter one that I use as a function:

using namespace System.Management.Automation
function Clone-Object ($InputObject) {
    <#
    .SYNOPSIS
    Use the serializer to create an independent copy of an object, useful when using an object as a template
    #>
    [psserializer]::Deserialize(
        [psserializer]::Serialize(
            $InputObject
        )
    )
}