microsoft / azure-pipelines-task-lib

Libraries for writing VSTS and TFS build tasks

Home Page:https://aka.ms/tfbuild

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Powershell functions for accessing secure files from secureFile library

gougeman opened this issue · comments

The powershell library needs an equivalent to SecureFileHelpers for use from powershell tasks. It should include methods for authenticating, downloading secure files to a temp location, etc.

I am interested in this too.

Yes please!

+1 to this, I also think there's no way to upload secure files programmatically to VSTS. That's definitely needed for anyone trying to fully automate secrets rotation relying on the secret file functionality.

In order to download a secure file you first need the secure file token. The linked PR will bring this into the PS SDK.
Once you have that something like the following will download your file:

    $uri = "$collectionUrl/$project/_apis/distributedtask/securefiles/$($secFileId)?ticket=$($secTicket)&download=true"

    Invoke-RestMethod -Uri $uri -UseDefaultCredentials -OutFile $fileName

Since the SDK version 0.11 you have the necessary to retrieve the secure file also from PS.

here an example

        $secFileId = Get-VstsInput -Name secureFile -Require
        $secTicket = Get-VstsSecureFileTicket -Id $secFileId
        $secName = Get-VstsSecureFileName -Id $secFileId
        $tempDirectory = Get-VstsTaskVariable -Name "Agent.TempDirectory" -Require
        $collectionUrl = Get-VstsTaskVariable -Name "System.TeamFoundationCollectionUri" -Require
        $project = Get-VstsTaskVariable -Name "System.TeamProject" -Require

        $filePath = Join-Path $tempDirectory $secName

        Invoke-RestMethod -Uri "$collectionUrl/$project/_apis/distributedtask/securefiles/$($secFileId)?ticket=$($secTicket)&download=true" -UseDefaultCredentials -OutFile $filePath

@stephenmichaelf or @ericsciple this can be closed

Thanks @mmajcica !

What value do I use for $secFileId?

I've tried using the guid identifier and just a simple "1". In both cases I don't get a value back from Get-VstsSecureFileTicket or Get-VstsSecureFileName.

Are there other steps I should be doing prior to using the code sample above?

@somefella123

You can check the following project of mine https://github.com/mmajcica/ImportCertificate

$secFileId value is brought from the UI:

...
{
      "name": "secureFile",
      "type": "secureFile",
      "label": "Secure File",
      "visibleRule": "importSource = secureFile",
      "defaultValue": "",
      "required": true,
      "helpMarkDown": "Select the secure file to download to a temporary location on the agent. The file will be cleaned up after the build or release."
    },
...

OK - that's moved me forward a little, thank you, but I still see something odd blocking my desired outcome. Do you have any suggestions?

The UI Control (type: secureFile) sends in a Guid - this is, as expected, the Id of the file.

If I use your code with the Id passed in from the UI control, then I can download the file.
If I use your code with a hardcoded Id that happens to match the Id that the UI control is passing in, then I can also download the file (this proves that hardcoded Ids 'should' work okay).

Now the odd behaviour: if I use your code with a hardcoded guid that is valid but represents a different file to the one selected in the UI then I don't get a secure file token / name back. If I then change the UI control to match the id that I've hardcoded, I am then able to access the new file (this proves my new hardcoded id was valid).

So it seems that the UI control of type secureFile is doing "something else" to the environment state to let me access that file.

Do you have any idea how I can remove this dependency on the secureFile UI control? Is there any other environmental magic I can use to let me download an arbitrary secure file, as I need to choose the file at run-time not design time.

Many thanks!

@mmajcica I'm trying to do similar to the extension you linked. However whenever I download the secure file, the content is ALWAYS in the following format:
{"id":"guid","name":"my.file"}

Any thoughts on what I might be doing incorrectly? I'm literally using the code you provided (copy/paste). I'm wondering if I need to add a scope to my manifest file to be able to access secure files.

EDIT: I had to add an Accept Header for application/octet-stream and now it works

Just to close the loop on this one - I experimented a fair bit with getting access to secure files, including looking at the Agent source. In short... you can't do it, you have to pre-select the file to download using the out of the box task (or your own task with an input parameter of type secureFiles).

This appears to add the Secure File to the agent's execution context, which then makes it available for the code given above.

If anyone else is trying to construct the secure file name via a variable, I suggest you go upvote this issue: microsoft/azure-pipelines-tasks#6885