Import-CSV, change incoming from string to int. Solved; is there another way?

Team PowerShell,

Is there a way to do this better?

Take incoming data from a CSV, validate if a value can be an integer type, and change to integer.

Use case is to deploy a VMware guest using new-vm command. Some parameters specify data type as int32

My csv file

<colgroup width="118"></colgroup> <colgroup width="175"></colgroup>
Properties Value
host vmhost1.company.com
vm_name accounting_test
ds_name dev_ds_1
vm_network vlan243
clone_from server-wk12-60gb
template template-w2k12r2-50gb
memory_gb 4
num_cpu 2
cores_per_socket 2
My hack, hacked from obscure, dusty places located in Google Land:
$csv = import-csv "C:\temp\vm_build.csv"

foreach ($item in $csv){

if( ($item.value -as [int]) -ne $null){      # evaluate if a string can be an integer

[int32]$item.value = $item.value}            # change to an integer

New-Variable $item.properties $item.value # create variables, populate with value
}

 

Happily, I get:

 

PS C:\powershell> $num_cpu.gettype()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Int32                                    System.ValueType


PS C:\powershell> $template.gettype()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     String                                   System.Object

You can do something like this:

$csv = @()
$csv += [pscustomobject]@{Properties='host';Value='vmhost1.company.com'}
$csv += [pscustomobject]@{Properties='memory_gb';Value=4}
$csv += [pscustomobject]@{Properties='num_cpu';Value=2}


$csv | Select Properties, @{Name='Value';Expression={if($_.Value -match "^[\d\.]+$"){[int]$_.Value}else{$_.Value}}}

Output:

PS C:\Users\Rob> $csv[0].Value.GetType()


IsPublic IsSerial Name                                     BaseType                                                                                                                                         
-------- -------- ----                                     --------                                                                                                                                         
True     True     String                                   System.Object                                                                                                                                    



PS C:\Users\Rob> $csv[1].Value.GetType()


IsPublic IsSerial Name                                     BaseType                                                                                                                                         
-------- -------- ----                                     --------                                                                                                                                         
True     True     Int32                                    System.ValueType                                                                                                                                 

But my question is why you want to do it. It’s not really necessary in Powershell as it will do most of these conversions occur dynamically, you don’t need to implicitly tell Powershell it needs to be an INT. Take this as an example:

function Test-It {
    param (
        [int]$MyNumber
    )

    $MyNumber.GetType()
}

$string = "123"
$string.GetType()
Test-It -MyNumber $string

You’ll see we define 123 as string, the GetType() will tell us it’s a string, but when we pass it as a parameter that is expecting an integer, it will do the conversion to INT there and return GetType() as an INT.

PS C:\Users\Rob> $string = "123"


PS C:\Users\Rob> $string.GetType()


IsPublic IsSerial Name                                     BaseType                                                                                                                                         
-------- -------- ----                                     --------                                                                                                                                         
True     True     String                                   System.Object                                                                                                                                    



PS C:\Users\Rob> Test-It -MyNumber $string

IsPublic IsSerial Name                                     BaseType                                                                                                                                         
-------- -------- ----                                     --------                                                                                                                                         
True     True     Int32                                    System.ValueType                                                                                                                                 

This is quite close to being one of the better ways to go already, you just need to clean up your code and double check your logic. As Rob mentions, you could also store things neatly in a hashtable or a similar construct, which will make it easier to find your values again. Programmatically generating names of variables is often a bit of a tiring exercise, because you will have to then find them again. Best to keep them all in one place, in one variable.

# Create empty hashtable as a container
$Table = @{}
foreach ($Item in $csv) {
    $IntValue = $Item.Value -as [int]
    # If the cast fails, $IntValue will be $null, and this statement will be skipped ($null casts to $false)
    if ($IntValue) {
        $Table.Add($Item.Properties, $Item.Value -as [int]
    }
}

Hi, Joel and Rob,

Thank you for your responses. I’m looking them over, as to learn.

I believe the root action of import-csv is everything comes in as a string; integer-shaped-objects are thusly not typed as [int]. To meet PoweCLI new-vm parameter requirements, e.g. [-CoresPerSocket Int32], [-NumCpu Int32], I gotta do some type manipulation.

Dynamically creating the variables from the CSV will map to the hash table that splats the parameters; perhaps some CSV configs have E and F drive, some CPU and memory requirements will differ, etc. Programming logic will IF for additional items, and call functions accordingly.

function import-VMParameters {
Import-CSV -path

New-Variable #validate for int, where int is required, create variables

Call connect-VCenter function

}

function connect-VCenter{

log in

call new-vmbuild function

}

function new-vmbuild{
if ($clone_from){
$build=@{
VMHost=$host
name=$vm_name
Datastore=$ds_name
networkname=$vm_network
VM=$clone_from
}
new-vm @build
# call set_CpuMem function
}
...and so it goes.
I believe the root action of import-csv is everything comes in as a string; integer-shaped-objects are thusly not typed as [int]. To meet PoweCLI new-vm parameter requirements, e.g. [-CoresPerSocket Int32], [-NumCpu Int32], I gotta do some type manipulation.

You are correct, everything imported from a CSV is a string. Look at the second example I provided, you don’t need to do type conversions. If the input can be converted to the type (e.g. [int]), Powershell will do it for you without explicitly defining the type before it’s passed. It doesn’t hurt anything, but you are doing unnecessary work IMHO as the New-VM function will do the same conversion you are doing manually. In both examples below, NumCpu is passed a string, but the conversion is handled by defining it as [int] in the function params:

New-VM -NumCpu "1"
New-VM -NumCpu '1'

You mention “parameter requirements” but if we’re being honest those are somewhat loose in PowerShell functions. Even if your value is a string, as long as it can be parsed as a number PowerShell will automatically convert it to the proper value when you pass into a numeric-typed function argument.

Pfffft, not work so hard? Sign me up! :slight_smile:

Thank you for clarifying that POSH will do the conversion to the type. That does make it easier. I’ll tuck away the manual “evaluate for int” for future use.

Cheers,

Seth