Forum Discussion

proxicon's avatar
proxicon
Icon for Altostratus rankAltostratus
Aug 23, 2022

Create a IFile {system level} via API - Powershell

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.

  • proxicon's avatar
    proxicon
    Aug 24, 2022

    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

     

     

     

  • 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)"
    • JRahm's avatar
      JRahm
      Icon for Admin rankAdmin

      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!

      • proxicon's avatar
        proxicon
        Icon for Altostratus rankAltostratus

        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

         

    • Patrik_Jonsson's avatar
      Patrik_Jonsson
      Icon for MVP rankMVP

      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"

       

      • proxicon's avatar
        proxicon
        Icon for Altostratus rankAltostratus

        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.