cancel
Showing results for 
Search instead for 
Did you mean: 

Create a IFile {system level} via API - Powershell

proxicon
Altostratus
Altostratus

Hi All,

Attempting the following:
1: Create iFile system level
2: Update existing iRule-> iFile referance the the file uploaded in step 1.

Im getting stuck at step 1, any assistance creatly apprecuated.
What I have tried:

Get Auth Token:

 

# Get API token
$big_ip          = 'https://[REDACTED]'
$url    = "{0}{1}" -f $big_ip, '/mgmt/shared/authn/login'
$body   = @{
    username = "[REDACTED]"
    password = '[REDACTED]'
    loginProviderName = "tmos"
} | ConvertTo-Json

$result = Invoke-RestMethod -Method 'POST' -Uri $url -ContentType 'application/json' -Body $body
$token = $($result.token.token)

 

Works.
Next, Upload my file:

 

$File_Name   = 'MyFile'
$File_Upload = 'C:\Temp\MyFile'
$url         = "{0}{1}{2}" -f $big_ip, "/mgmt/shared/file-transfer/uploads/", $File_Name
$filelength  = (Get-Item $File_Upload).length

$headers = @{
    'Content-Type' = 'application/octet-stream'
    'X-F5-Auth-Token' = $token
    'Content-Range' = "0-$($filelength-1)/$filelength"
}

$result = Invoke-RestMethod -Method Post -Uri $url -Headers $headers -InFile $File_Upload

 

Works fine too I am returned with:

 

remainingByteCount : 0
usedChunks         : @{0=46321}
totalByteCount     : 46321
localFilePath      : /var/config/rest/downloads/MyFile
temporaryFilePath  : /var/config/rest/downloads/tmp/MyFile
generation         : 0
lastUpdateMicros   : 1661257236246203

 

Next is where im stuck, creation of the iFile system level from the uploaded file.
Ive re-typed the below from the Curl samples here: Syncing local repositories and ifiles using iContr... - DevCentral (f5.com)

 

### Create a iFile {system level} - does not yet work
$File_Name = 'MyFile'
$url       = "{0}{1}{2}" -f $big_ip, "/mgmt/tm/sys/file/ifile/", $File_Name

$headers = @{
    'Content-Type'    = 'application/json'
    'X-F5-Auth-Token' = $token
}

$body = @{
    'name'            = $File_Name
    'source-path'     = "file:///var/config/rest/downloads/$File_Name"
} | ConvertTo-Json

$result = Invoke-RestMethod -Method put -Uri $url -Headers $headers -Body $body

 

Next I am recieving that the file cannot be found. So this query is incorrectly tructured ?
Translated these examples to powershell:
Syncing local repositories and ifiles using iContr... - DevCentral (f5.com)

 

Invoke-RestMethod : {"code":404,"message":"01020036:3: The requested iFile (/Common/MyFile) was not found.","errorStack":[],"apiError":3}
At line:1 char:11
+ $result = Invoke-RestMethod -Method put -Uri $url -Headers $headers - ...
+           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod], WebException
    + FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand

 

 Any assistance appreated with the above & next up updating the iRule file refence.

1 ACCEPTED SOLUTION

Found my fault... @JRahm & @Patrik_Jonsson this was after my long reply to your other post @JRahm (and yes it works on a POST method)... I had the file name in the URL overload specified on the initial create request, it should not be in there...

so this..

 

 

$File_Name = 'MyFile'
$url       = "{0}{1}{2}" -f $big_ip, "/mgmt/tm/sys/file/ifile/",$File_Name

$headers = @{
    'Content-Type'    = 'application/json'
    'X-F5-Auth-Token' = $token
}

$body = @{
    'name'            = $File_Name
    'source-path'     = "file:/var/config/rest/downloads/$File_Name"
} | ConvertTo-Json -Compress

$result = Invoke-RestMethod -Method post -Uri $url -Headers $headers -Body $body 

 

 


needs to be:

 

 

$File_Name = 'MyFile'
$url       = "{0}{1}" -f $big_ip, "/mgmt/tm/sys/file/ifile/"

$headers = @{
    'Content-Type'    = 'application/json'
    'X-F5-Auth-Token' = $token
}

$body = @{
    'name'            = $File_Name
    'source-path'     = "file:/var/config/rest/downloads/$File_Name"
} | ConvertTo-Json -Compress

$result = Invoke-RestMethod -Method post -Uri $url -Headers $headers -Body $body

 

 

 without the file name in the initial overload.

Hope this helps some future person 🙂 
Off to the next challange.

Bonus: Retyped to splat (overload definition) - because why not.

 

 

# Create a IFile {system level} 
$File_Name = 'MyFile'
$Uri       = "{0}{1}" -f $big_ip, "/mgmt/tm/sys/file/ifile/"

# Construct overload definition
$Param_InvokeRestMethod = @{
    headers = @{
        'Content-Type'    = 'application/json'
        'X-F5-Auth-Token' = $token
    }

    body = @{
        'name'            = $File_Name
        'source-path'     = "file:/var/config/rest/downloads/$File_Name"
    } | ConvertTo-Json -Compress

    Method = 'post'

    Uri = $Uri
}

# Post & capture result
$result = Invoke-RestMethod @Param_InvokeRestMethod

 

 

 

View solution in original post

10 REPLIES 10

Hi there!

Nice to see some Powershell users in the forum. 🙂 If you have validated that the file is actually there, try this to make sure that it's not SELinux that is tripping you up:

restorecon -RvF <file path>

Ps. Coming from Python2? Recommending string interpolation:

$url = "{0}{1}{2}" -f $big_ip, "/mgmt/tm/sys/file/ifile/", $File_Name
# Becomes
$url = "$big_ip/mgmt/tm/sys/file/ifile/$File_Name"
# A bit safer (less risk in terms of unintentionally referencing the wrong variable):
$url = "$($big_ip)/mgmt/tm/sys/file/ifile/$($File_Name)"

Also, try this for the source-path property when creating the body:

"file:/var/config/rest/downloads/$File_Name"
# instead of
"file:///var/config/rest/downloads/$File_Name"

 

Thanks Patrick, ive tried both variations before typing this initial post. Saw tht in a few other examples & as with all things code if the API docs did not deliver, we are on stack overflow or forums discussing alternatives 🙂 

using file:/// or file:/ serves up the same API 400 exit 37

 

Invoke-RestMethod : {"code":400,"message":"Failed! exit_code (37).\n","errorStack":[],"apiError":26214401}
At line:1 char:11
+ $result = Invoke-RestMethod -Method put -Uri $url -Headers $headers - ...
+           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod], WebException
    + FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand

 

This is with:

 

$File_Name = 'MyFile'
$url       = "{0}{1}{2}" -f $big_ip, "/mgmt/tm/sys/file/ifile/", $File_Name

$headers = @{
    'Content-Type'    = 'application/json'
    'X-F5-Auth-Token' = $token
}

$body = @{
    'name'            = $File_Name
    'source-path'     = "file:/var/config/rest/downloads/tmp/$File_Name"
} | ConvertTo-Json -Compress

$result = Invoke-RestMethod -Method put -Uri $url -Headers $headers -Body $body

 

Both instances the file upload occured & exists in:

 

remainingByteCount : 0
usedChunks         : @{0=46321}
totalByteCount     : 46321
localFilePath      : /var/config/rest/downloads/MyFile
temporaryFilePath  : /var/config/rest/downloads/tmp/MyFile
generation         : 0
lastUpdateMicros   : 1661327626220187

 


Point taken on the string formatting,  I flip between C# & Powershell quite ofen so the formatting is more a C# habbit than Python for me.

All of these examples are "try before writinig a function" code so these samples arent set in stone & the eventual final solution & battle tested functions & modules will be written & deployed towards the end of this excersisise 🙂 (when I get the samples working)

Thank for your reply.

create method should be a post as well, not a put. Maybe that's what you have, as I don't speak powershell very well. 🙂

For the python implementation of what you're trying to do, you can read the functional test of the python SDK here.

And might I add, @proxicon, that that's a very well asked question! Great details. THANK YOU!

Nice catch with the method! 🙂

Found my fault... @JRahm & @Patrik_Jonsson this was after my long reply to your other post @JRahm (and yes it works on a POST method)... I had the file name in the URL overload specified on the initial create request, it should not be in there...

so this..

 

 

$File_Name = 'MyFile'
$url       = "{0}{1}{2}" -f $big_ip, "/mgmt/tm/sys/file/ifile/",$File_Name

$headers = @{
    'Content-Type'    = 'application/json'
    'X-F5-Auth-Token' = $token
}

$body = @{
    'name'            = $File_Name
    'source-path'     = "file:/var/config/rest/downloads/$File_Name"
} | ConvertTo-Json -Compress

$result = Invoke-RestMethod -Method post -Uri $url -Headers $headers -Body $body 

 

 


needs to be:

 

 

$File_Name = 'MyFile'
$url       = "{0}{1}" -f $big_ip, "/mgmt/tm/sys/file/ifile/"

$headers = @{
    'Content-Type'    = 'application/json'
    'X-F5-Auth-Token' = $token
}

$body = @{
    'name'            = $File_Name
    'source-path'     = "file:/var/config/rest/downloads/$File_Name"
} | ConvertTo-Json -Compress

$result = Invoke-RestMethod -Method post -Uri $url -Headers $headers -Body $body

 

 

 without the file name in the initial overload.

Hope this helps some future person 🙂 
Off to the next challange.

Bonus: Retyped to splat (overload definition) - because why not.

 

 

# Create a IFile {system level} 
$File_Name = 'MyFile'
$Uri       = "{0}{1}" -f $big_ip, "/mgmt/tm/sys/file/ifile/"

# Construct overload definition
$Param_InvokeRestMethod = @{
    headers = @{
        'Content-Type'    = 'application/json'
        'X-F5-Auth-Token' = $token
    }

    body = @{
        'name'            = $File_Name
        'source-path'     = "file:/var/config/rest/downloads/$File_Name"
    } | ConvertTo-Json -Compress

    Method = 'post'

    Uri = $Uri
}

# Post & capture result
$result = Invoke-RestMethod @Param_InvokeRestMethod

 

 

 

Thanks for sharing the solution for future users!

JRahm
Community Manager
Community Manager

glad you worked it out!

Heres a litte expansion on my full end to end scenario I faced and the solution for some future person to find.

Upload a file with x,y,z contents on a regular basis with a new system level file version (date-time+version) an mapped to the LTM object served to clients

0: Retrieve authentication token
1: Read existing I files in order to obtain & increment the version (not using the F5 incremental but insteaf a static file every time)
2: Create iFile system level object and referance the uploaded file
3: Update existing iRule-> iFile referance the the system level iFile uploaded in step 1/2

#Region Declaration
# Folders & files
$Folder_Path     = 'C:\files\'
$File_Upload     = "$Folder_Path\MyFile"
$DT_TodaysDate   = Get-Date -Format "yyyyMMdd"
$File_Name       = 'lan_pac_{date}_v{version}'
$File_Name       = $File_Name.Replace('{date}', $DT_TodaysDate)
$File_Name       = $File_Name.Replace('{version}', $Version)

$F5LTM_HostIP    = 'https://[REDACTED]'
$F5LTM_HostUser  = '[REDACTED]'
$F5LTM_HostPass  = '[REDACTED]'
#EndRegion

#Region Get API token
$Paramater_InvokeRestMethod = @{
    Uri            = "{0}{1}" -f $F5LTM_HostIP, '/mgmt/shared/authn/login'
    'ContentType' = 'application/json'
    body   = @{
        username          = $F5LTM_HostUser
        password          = $F5LTM_HostPass
        loginProviderName = "tmos"
    } | ConvertTo-Json -Compress
    Method = 'Post'
}

$result = Invoke-RestMethod @Paramater_InvokeRestMethod
${X-F5-Auth-Token} = $result.token.token

#EndRegion

#Region Get list of sys file iFiles & calculate the next version
$Paramater_InvokeRestMethod = @{
    Uri     = "{0}{1}" -f $F5LTM_HostIP,'/mgmt/tm/sys/file/ifile'
    headers = @{
        'X-F5-Auth-Token' = ${X-F5-Auth-Token}
    }
    Method  = 'Get'
}

$result = Invoke-RestMethod @Paramater_InvokeRestMethod

#Calculate the next file version
[Int]$Version = $result.items.name | ForEach-Object{
    ([String]$PSItem).split('v') | Select-Object -Last 1
} | Sort-Object -Descending -Unique | Select-Object -First 1

# Increment version
$Version++

#EndRegion

#Region Upload the next revision file to F5
$File_Name   = $File_Name.Replace('{version}', $Version)

# Calculate file length
$Filelength = (Get-Item $File_Upload).length

# Create Overload
$Param_InvokeRestMethod = @{
    Uri     = "{0}{1}{2}" -f $F5LTM_HostIP, "/mgmt/shared/file-transfer/uploads/", $File_Name
    headers = @{
        'Content-Type' = 'application/octet-stream'
        'X-F5-Auth-Token' = ${X-F5-Auth-Token}
        'Content-Range' = "0-$($filelength-1)/$filelength"
    }
    Method = 'Post'
    InFile = $File_Upload
}

try {
    $result = Invoke-RestMethod @Param_InvokeRestMethod

} catch [Exception] {
    # Can't capture the response code with Invoke-RestMethod, but can catch the expection and assume failure.
    Write-Host "StatusCode:" $_.Exception.Response.StatusCode.value__
    Write-Host "StatusDescription:" $_.Exception.Response.StatusDescription 
    
    Write-Host $_.Exception
    exit
}

#EndRegion

#Region Create a IFile {system level} 
$Uri       = "{0}{1}" -f $F5LTM_HostIP, "/mgmt/tm/sys/file/ifile/"

# Construct overload definition
$Param_InvokeRestMethod = @{
    Uri     = $Uri
    headers = @{
        'Content-Type'    = 'application/json'
        'X-F5-Auth-Token' = ${X-F5-Auth-Token}
    }
    body    = @{
        'name'            = $File_Name
        'source-path'     = "file:/var/config/rest/downloads/$File_Name"
    } | ConvertTo-Json -Compress
    Method  = 'post'
}

# Post & capture result
$result = Invoke-RestMethod @Param_InvokeRestMethod

#EndRegion

#Region Creating an ifile object (LTM level) Not required unless there is no LTM file

$url = "{0}{1}" -f $F5LTM_HostIP, "/mgmt/tm/ltm/ifile/"
$headers = @{    
    'X-F5-Auth-Token' = ${X-F5-Auth-Token}
    'Content-Type'    = 'application/json'
}
$body = @{
    'name'            = $File_Name
    'file-name'       = $File_Name
} | ConvertTo-Json -Compress

$result = Invoke-RestMethod -Method post -Uri $url -Headers $headers -body $body

#EndRegion

#Region Updating an ifile object (LTM level) required to update the existing iFile LTM object referance to the new file
$Param_InvokeRestMethod = @{
    Uri = '{0}{1}' -f $F5LTM_HostIP, '/mgmt/tm/ltm/ifile/client-file'
    Headers = @{    
        'X-F5-Auth-Token' = ${X-F5-Auth-Token}
        'Content-Type'    = 'application/json'
    }
    Body = @{
        'name'            = 'client-file'
        'file-name'       = $File_Name
    } | ConvertTo-Json -Compress
    Method = 'Put'
}

$result = Invoke-RestMethod @Param_InvokeRestMethod

 

Thanks for your reply @JRahm ,

I have tried a Post as opposed to a Put method call & the API return message is different. Maybe a hint at what im doing wrong ?

With Put:

{"code":400,"message":"Failed! exit_code (37).\n","errorStack":[],"apiError":26214401}

With Post:

{"code":400,"message":"Found unexpected URI tmapi_mapper/sys/file/ifile/MyFile.","errorStack":[],"apiError":1}

I can add that my authenticated user I get the API token with is a radius authenticated user and not local. The only diff ive seen with thee Curl examples ive followed/mentioned here + the python wrapper mentionred (which I tried to re-type to powershell as well) are that they use a local user with the "Authentication: Basic user:pass" header as opposed to 'X-F5-Auth-Token' in the header which im using.

I can manually create a system level iFile manually in the UI with this radius user (this is what im trying to automate via the API) & use the iControl Reest API to upload new content into the iFile, this works for me using the X-F5-Auth-Token radius user. So the writing of new content to an existing iFile works for me but not the creation thereof not.

I have access to a few/newer F5-LTM, my network SME granted me access to a V15.1.5 F5-LTM to rule out a possible issue with the F5 12.1.5 im working with at the minute (K69621049) which was resolved in revision 12.1.5 I initially thought that this was my issue. Both revisions of F5 I can test against (15.1.5 & 12.1.5) Im observing the exact same return codes from the API as at the top of this post.

Taken the above into account, can this be an issue for me that the radius / remote user might have an issue on this method of the API & is it worth asking my network SME to try create a local account to rule this out?

Ive tried constructing the "Authentication: Basic user:pass" header with my radius user but get a 401 authentication failed. So I feel I needed to specify this in the initial post but it was omitted due to loads of typing 🙂

Invoke-RestMethod : {"code":401,"message":"Authorization failed: user=https://localhost/mgmt/shared/authz/users/MyUser resource=/mgmt/tm/sys/file/ifile/MyFile verb=POST uri:http://localhost:8100/mgmt/tm/sys/file/ifile/MyFile
[REDACTEDIP]referrer:[REDACTEDIP]sender:[REDACTEDIP]","referer":"[REDACTEDIP]","restOperationId":122408205,"errorStack":["java.lang.SecurityException: Authorization failed: user=https://localhost/mgmt/shared/authz/users/ccfortinetsrv
resource=/mgmt/tm/sys/file/ifile/MyFile verb=POST uri:http://localhost:8100/mgmt/tm/sys/file/ifile/MyFile referrer:[REDACTEDIP] sender:[REDACTEDIP]","at
com.f5.rest.workers.EvaluatePermissions.failPermissionValidation(EvaluatePermissions.java:236)","at com.f5.rest.workers.EvaluatePermissions.completeEvaluatePermission(EvaluatePermissions.java:198)","at
com.f5.rest.workers.EvaluatePermissions.evaluatePermission(EvaluatePermissions.java:78)","at com.f5.rest.workers.ForwarderPassThroughWorker.onForward(ForwarderPassThroughWorker.java:227)","at
com.f5.rest.workers.ForwarderPassThroughWorker.onPost(ForwarderPassThroughWorker.java:502)","at com.f5.rest.common.RestWorker.callDerivedRestMethod(RestWorker.java:1120)","at com.f5.rest.common.RestWorker.callRestMethodHandler(RestWorker.java:1034)","at
com.f5.rest.common.RestServer.processQueuedRequests(RestServer.java:1355)","at com.f5.rest.common.RestServer.access$000(RestServer.java:45)","at com.f5.rest.common.RestServer$1.run(RestServer.java:316)","at
java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:473)","at java.util.concurrent.FutureTask.run(FutureTask.java:262)","at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:178)","at
java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:292)","at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1152)","at
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:622)","at java.lang.Thread.run(Thread.java:748)\n"],"kind":":resterrorresponse"}
At line:1 char:11