0

I have a script which I'm trying to modify, which adds objects to a firewall via powershell, using Invoke-RestMethod

The current script has the following code;

#Import CSV and set variables
$csv = import-csv C:\Powershell\groups.csv

# RESTful API Call
$csv | ForEach-Object {
            $Name = $_.name
            $Member1 =$_.member1
            $Member2 =$_.member2
            Invoke-RestMethod -Uri https://172.16.16.16:4444/webconsole/APIController?reqxml=<Request><Login><Username>admin</Username><Password>password</Password></Login><Set%20operation=%27add%27><IPHostGroup><Name>$Name</Name><IPFamily>IPv4</IPFamily><HostList><Host>$Member1</Host><Host>$Member2</Host></IPHostGroup></Set></Request>
} 

I am wanting to import the hostgroups via groups.csv which (in my test) has 3 columns as follows;

Name,Member1,Member2
TestGroup,TestHost1,TestHost2
TestGroup2,TestHost3,TestHost4

etc.

My problem is that in the real data, there are varying amount of hosts in each group, some have hundreds. I'm not sure how to get these into the command without defining a variable for each possible member. Even then, say I created $Member(s) all the way to 200 (be gentle, I'm not a real coder!) and then imported them in manually one by one in the Invoke-Restmethod command (Might as well do it by hand at that point!) I'm not sure the command would handle the blank inputs in the cases where there were only a few hosts in the group.

(In other words if my csv had the following entries;)

Name,Member1,Member2,Member3,Member4
TestGroup,TestHost1,TestHost2,TestHost3,TestHost4
TestGroup2,TestHost5,TestHost6
TestGroup3,TestHost7

And I did;

# RESTful API Call
$csv | ForEach-Object {
            $Name = $_.name
            $Member1 =$_.member1
            $Member2 =$_.member2
            $Member3 =$_.member3
            $Member4 =$_.member4

The Rest call for the third group would end up running as;

Invoke-RestMethod -Uri https://172.16.16.16:4444/webconsole/APIController?reqxml=<Request><Login><Username>admin</Username><Password>password</Password></Login><Set%20operation=%27add%27><IPHostGroup><Name>TestGroup3</Name><IPFamily>IPv4</IPFamily><HostList><Host>TestHost7</Host><Host></Host><Host></Host><Host></Host></IPHostGroup></Set></Request>

Can anyone point me in the direction of a better way of doing this?

MatF
  • 13
  • 4
  • A properly formatted csv file has headers you could enumerate like [this answer](https://stackoverflow.com/a/25764540/6811411) –  Apr 16 '18 at 18:36
  • The problem is it's being saved as CSV from an Excel export of the previous firewall. When I save as CSV, it just adds blank headers for each additional column (originally the objects for each group were in the same column in seperate rows, I transposed them with copy / paste manually). I'll read through that answer and try understand it though, thanks! – MatF Apr 16 '18 at 18:45

1 Answers1

0

You can get all member names using .PSObject.Properties.Name

Example:

$Csv = Import-Csv -Path 'C:\Powershell\groups.csv'

# Request XML template
$RequestTpl = @'
<Request>
<Login>
<Username>admin</Username>
<Password>password</Password>
</Login>
<Set%20operation=%27add%27>
<IPHostGroup>
<Name>{0}</Name>
<IPFamily>IPv4</IPFamily>
<HostList>
{1}
</HostList>
</IPHostGroup>
</Set>
</Request>
'@

# Host list XML template
$RequestHostListTpl = '<Host>{0}</Host>'

$Csv | ForEach-Object {
    <#
        Get names of all the properties in the current object
        Leave only those that don't match '^Name$' regex.
        -match, when operates on collections, returns matched items
        You can use

        $_.PSObject.Properties.Name | Where-Object {$_ -ne 'Name'}

        but it's a bit slower.
    #>
    $Members = @($_.PSObject.Properties.Name) -notmatch '^Name$'

    # Build list of hosts
    $RequestHostList = foreach ($item in $Members) {
        # Only add item if it's not empty
        if ($_.$item) {
            $RequestHostListTpl -f $_.$item
        }
    }

    # Build request XML
    $Request = $RequestTpl -f $_.Name, -join $RequestHostList

    # Remove newlines to make it one long string
    $Request = $Request -replace '\r|\n'

    # Show resulting url
    "Invoke-RestMethod -Uri https://172.16.16.16:4444/webconsole/APIController?reqxml=$Request"

    # Uncomment to actually invoke API call
    #Invoke-RestMethod -Uri "https://172.16.16.16:4444/webconsole/APIController?reqxml=$Request"
} 
beatcracker
  • 6,714
  • 1
  • 18
  • 41