Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AzureKeyVaultV2 task #14259

Merged
merged 12 commits into from
Apr 14, 2021
2 changes: 2 additions & 0 deletions .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ Tasks/AzureIoTEdgeV2/ @marianan @tameraw @pprovost

Tasks/AzureKeyVaultV1/ @ammohant

Tasks/AzureKeyVaultV2/ @ammohant @tauhid621

Tasks/AzureMonitorAlertsV0/ @nadesu @chshrikh @pipeline-environment-team

Tasks/AzureMonitorV0/ @ammohant
Expand Down
77 changes: 77 additions & 0 deletions Tasks/AzureKeyVaultV2/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@

tauhid621 marked this conversation as resolved.
Show resolved Hide resolved

# Azure Key Vault Task

### Overview

This task is used for downloading secrets (such as authentication keys, storage account keys, data encryption keys, .PFX files, and passwords) from a given [Azure key vault](https://docs.microsoft.com/en-us/rest/api/keyvault/about-keys--secrets-and-certificates?redirectedfrom=MSDN#key-vault-secrets-1) instance. This can be used to fetch the latest values of all/subset of secrets from the vault and set them as task variables which can be consumed in the following tasks. Task is node based and works with Xplat agents (Windows, Linux or OSX).

## Contact Information

Please report a problem at [Developer Community Forum](https://developercommunity.visualstudio.com/spaces/21/index.html) if you are facing problems in making this task work. You can also share feedback about the task like, what more functionality should be added to the task, what other tasks you would like to have, at the same place.

### What's new in Version 2.0
- Added support for %3B, %5D in secrets.

## Pre-requisites for the task

The following pre-requisites need to be setup for the task to work properly.

#### Azure Subscription

To deploy to Azure, an Azure subscription has to be linked to Team Foundation Server or to Azure Pipelines using the Services tab in the Account Administration section. Add the Azure subscription to use in the Build or Release Management definition by opening the Account Administration screen (gear icon on the top-right of the screen) and then click on the Services Tab. Create a service endpoint of 'Azure Resource Manager' type. For more troubleshooting guidance around endpoint creation, refer [this](https://www.visualstudio.com/en-us/docs/build/actions/azure-rm-endpoint).

For Azure MSDN accounts, one can either use a [Service Principal](https://go.microsoft.com/fwlink/?LinkID=623000&clcid=0x409) or a work account. It's easy to create a work account as shown below:

1. Create an user in the Azure Active Directory from the [portal](https://msdn.microsoft.com/en-us/library/azure/hh967632.aspx) (this is the old Azure portal). After adding the account, the following two things need to be done to use the organization in Azure Pipelines:
- Add the Active Directory account to the co-administrators in the subscription. Go to the Settings and then click on administrators and add the account as a co-admin like, [[email protected]](mailto:[email protected])
- Login to the portal with this Active Directory account wiz. [[email protected]](mailto:[email protected]), and change the password. Initially a temporary password is created and that needs to be changed at the first login.
2. Add that user and password in the service connections in Azure Pipelines and deployments will work with that account.

#### Pre-existing Azure Key Vault with secrets

This task supports fetching latest values of the secrets which are already added to a pre-existing key vault instance. If there is no pre-existing Key vault, you can create a key vault in the the [Azure portal](https://ms.portal.azure.com/#create/Microsoft.KeyVault) or use [Azure PowerShell](https://docs.microsoft.com/en-us/azure/key-vault/key-vault-get-started#a-idvaultacreate-a-key-vault) or use [Azure CLI](https://docs.microsoft.com/en-us/azure/key-vault/key-vault-manage-with-cli2#create-a-key-vault).

To add secrets to the keyvault, use PowerShell cmdlet [Set-AzureKeyVaultSecret](https://docs.microsoft.com/en-us/powershell/module/azurerm.keyvault/set-azurekeyvaultsecret?view=azurermps-4.0.0): If the secret does not exist, this cmdlet creates it. If the secret already exists, this cmdlet creates a new version of that secret.

Or use Azure CLI : To add a secret, which is a password named SQLPassword and that has the value of Pa$$w0rd to Azure Key Vault, type the following:

```
az keyvault secret set --vault-name 'ContosoKeyVault' --name 'SQLPassword' --value 'Pa$$w0rd'
```

### Parameters of the task:

The parameters of the task are described below. The parameters listed with a \* are required parameters for the task:

* **Azure Subscription**\*: Select the service endpoint for the Azure Subscription where the Azure Key vault instance is created. To configure new service endpoint, select the Azure subscription from the list and click 'Authorize'. If your subscription is not listed or if you want to use an existing Service Principal, you can setup an Azure service connection using 'Manage' link.

Ensure the Azure endpoint has at least Get and List permissions for Secrets on the vault. You can set these permissions in the Azure portal: Open the Settings blade for the vault, choose Access policies, then Add new. In the Add access policy blade, choose Select principal and select the service principal for your client account. In the Add access policy blade, choose `Secret permissions` and ensure that Get and List are checked (ticked). Choose OK to save the changes.

* **Key Vault**\*: Select the name of the Key vault from which the secrets need to be downloaded.

* **Secrets filter**\*: Provide a comma separated list of secret names or use the default value `*` to download all secrets from the selected key vault. This can be used to fetch the latest values of all/subset of secrets from the vault and set them as task variables which can be consumed in the following tasks.

For example, if there is a secret name: connectionString, a task variable `$(connectionString)` is created with the latest fetched value of the respective secret from Azure key vault. And this secret variable would be available to be consumed in subsequent tasks.

If it is a certificate (example: a PFX file) that is fetched from the vault, then the task variable would contain the content of the PFX in string format. To retrieve the PFX file from the task variable, the following sample PowerShell code can be used (after passing the certificate variable as a parameter to the script):

```powershell
# Task parameters: $(PfxSecret)
param ($pfxSecretStringValue)
$kvSecretBytes = [System.Convert]::FromBase64String($pfxSecretStringValue)
$certCollection = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2Collection
$certCollection.Import($kvSecretBytes, $null, [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::Exportable)
```

If the certificate file needs to be stored on the hard disk then it is good practice to encrypt it with a password:

```powershell
# Get the file created
$password = <password>
$protectedCertificateBytes = $certCollection.Export([System.Security.Cryptography.X509Certificates.X509ContentType]::Pkcs12, $password)
$pfxPath = [Environment]::GetFolderPath("Desktop") + "\MyCert.pfx"
[System.IO.File]::WriteAllBytes($pfxPath, $protectedCertificateBytes)
```

More help can be found [here](https://blogs.technet.microsoft.com/kv/2016/09/26/get-started-with-azure-key-vault-certificates).
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
{
"loc.friendlyName": "Azure Key Vault",
"loc.helpMarkDown": "[Learn more about this task](https://go.microsoft.com/fwlink/?linkid=848891)",
"loc.description": "Azure Key Vault-Geheimnisse herunterladen",
"loc.instanceNameFormat": "Azure Key Vault: $(KeyVaultName)",
"loc.releaseNotes": "Funktioniert mit plattformübergreifenden Agents (Linux, macOS, Windows)",
"loc.input.label.ConnectedServiceName": "Azure-Abonnement",
"loc.input.help.ConnectedServiceName": "Wählen Sie das Azure-Abonnement für den Schlüsseltresor aus.",
"loc.input.label.KeyVaultName": "Schlüsseltresor",
"loc.input.help.KeyVaultName": "Geben Sie den Namen eines vorhandenen Schlüsseltresors an.",
"loc.input.label.SecretsFilter": "Geheimnisfilter",
"loc.input.help.SecretsFilter": "Eine durch Trennzeichen getrennte Liste von Geheimnisnamen, oder verwenden Sie *, um alle Geheimnisse aus dem ausgewählten Schlüsseltresor herunterzuladen.",
"loc.messages.ClientIdCannotBeEmpty": "\"clientId\" darf keine leere Zeichenfolge sein.",
"loc.messages.DomainCannotBeEmpty": "\"domain\" darf keine leere Zeichenfolge sein.",
"loc.messages.SecretCannotBeEmpty": "\"secret\" darf keine leere Zeichenfolge sein.",
"loc.messages.armUrlCannotBeEmpty": "Die ARM-URL darf keine leere Zeichenfolge sein.",
"loc.messages.authorityUrlCannotBeEmpty": "\"authority\" darf keine leere Zeichenfolge sein.",
"loc.messages.CallbackCannotBeNull": "Rückruf darf nicht NULL sein.",
"loc.messages.CredentialsCannotBeNull": "\"credentials\" darf nicht NULL sein.",
"loc.messages.SubscriptionIdCannotBeNull": "\"subscriptionId\" darf nicht NULL sein.",
"loc.messages.InvalidResponseLongRunningOperation": "Ungültige Antwort empfangen zum Abrufen des Status einer Aufgabe mit langer Laufzeit.",
"loc.messages.TimeoutWhileWaiting": "Zeitüberschreitung während des Wartens",
"loc.messages.ResourceGroupCannotBeNull": "\"resourceGroupName\" darf nicht NULL oder undefiniert sein und muss den Typ \"Zeichenfolge\" aufweisen.",
"loc.messages.ResourceGroupExceededLength": "\"resourceGroupName\" muss der Einschränkung entsprechen – \"MaxLength\": 90",
"loc.messages.ResourceGroupDeceededLength": "\"resourceGroupName\" muss der Einschränkung entsprechen – \"MinLength\": 1",
"loc.messages.ResourceGroupDoesntMatchPattern": "\"resourceGroupName\" muss der Einschränkung entsprechen – \"Muster\": /^[-\\w\\._\\(\\)]+$/",
"loc.messages.AzKv_ConstructorFailed": "Fehler bei der Initialisierung der Azure Key Vault-Parameter. Fehler: %s.",
"loc.messages.SubscriptionIdLabel": "SubscriptionId: %s.",
"loc.messages.KeyVaultNameLabel": "Name des Schlüsseltresors: %s.",
"loc.messages.DownloadingSecretsUsing": "Geheimnisse werden heruntergeladen mit %s.",
"loc.messages.GetSecretsFailed": "Fehler beim Abrufen der Geheimnisse. Fehler: %s.",
"loc.messages.NoSecretsFound": "Es wurden keine Geheimnisse in %s gefunden.",
"loc.messages.NumberOfSecretsFound": "Anzahl von Geheimnissen, die in %s gefunden wurden: %s",
"loc.messages.NumberOfEnabledSecretsFound": "Anzahl aktivierter und nicht abgelaufener Geheimnisse, die in %s gefunden wurden: %s",
"loc.messages.DownloadingSecretValue": "Der Geheimniswert für %s wird heruntergeladen.",
"loc.messages.AccessDeniedError": "%s. Die angegebene Azure-Dienstverbindung benötigt die Berechtigungen \"Get\" und \"List\" für die Verwaltung von Geheimnissen für den ausgewählten Schlüsseltresor. Zum Festlegen dieser Berechtigungen laden Sie das Skript \"ProvisionKeyVaultPermissions.ps1\" aus den Build-/Releaseprotokollen herunter, und führen Sie es aus. Alternativ dazu können Sie sie auch über das Azure-Portal festlegen.",
"loc.messages.GetSecretValueFailed": "Fehler beim Abrufen des Geheimniswerts für %s. Fehler: %s.",
"loc.messages.ConflictingVariableFound": "Die Variable mit dem Namen %s ist sowohl in der Umgebung als auch dem Schlüsseltresor definiert.",
"loc.messages.GetSecretFailedBecauseOfInvalidCharacters": "Geheimnis nicht gefunden: %s. Der Geheimnisname muss eine Zeichenfolge sein, die zwischen 1 und 127 Zeichen lang ist und nur die folgenden Zeichen enthalten darf: -, 0–9, a–z, A–Z.",
"loc.messages.UploadingAttachment": "\"%s\" wird als Anlage hochgeladen",
"loc.messages.CouldNotWriteToFile": "Inhalt konnte nicht in Datei gespeichert werden. Fehler: %s",
"loc.messages.CouldNotMaskSecret": "Der Wert \"%s\" umfasst reguläre Ausdrücke und konnte deshalb nicht vollständig maskiert werden.",
"loc.messages.CouldNotFetchAccessTokenforAzureStatusCode": "Ein Zugriffstoken für Azure konnte nicht abgerufen werden. Statuscode: %s, Statusmeldung: %s",
"loc.messages.CouldNotFetchAccessTokenforMSIDueToMSINotConfiguredProperlyStatusCode": "Ein Zugriffstoken für den verwalteten Dienstprinzipal konnte nicht abgerufen werden. Konfigurieren Sie die verwaltete Dienstidentität (MSI) für den virtuellen Computer (https://aka.ms/azure-msi-docs). Statuscode: %s, Statusmeldung: %s",
"loc.messages.CouldNotFetchAccessTokenforMSIStatusCode": "Ein Zugriffstoken für den verwalteten Dienstprinzipal konnte nicht abgerufen werden. Statuscode: %s, Statusmeldung: %s",
"loc.messages.RetryingWithVaultResourceIdFromResponse": "Wiederholungsversuch mit der aus der Antwort abgerufenen Tresorressourcen-ID: %s",
"loc.messages.ExpiredServicePrincipal": "Das Zugriffstoken für Azure konnte nicht abgerufen werden. Stellen Sie sicher, dass der verwendete Dienstprinzipal gültig und nicht abgelaufen ist."
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
{
"loc.friendlyName": "Azure Key Vault",
"loc.helpMarkDown": "[Learn more about this task](https://go.microsoft.com/fwlink/?linkid=848891)",
"loc.description": "Download Azure Key Vault secrets",
"loc.instanceNameFormat": "Azure Key Vault: $(KeyVaultName)",
"loc.releaseNotes": "What's new in Version 2.0: <br/>&nbsp;Added support for %3B, %5D in secrets",
"loc.input.label.ConnectedServiceName": "Azure subscription",
"loc.input.help.ConnectedServiceName": "Select the Azure subscription for the key vault",
"loc.input.label.KeyVaultName": "Key vault",
"loc.input.help.KeyVaultName": "Provide the name of an existing key vault",
"loc.input.label.SecretsFilter": "Secrets filter",
"loc.input.help.SecretsFilter": "Comma separated list of secret names or leave * to download all secrets from the selected key vault.",
"loc.input.label.RunAsPreJob": "Make secrets available to whole job",
"loc.input.help.RunAsPreJob": "Run the task before job execution begins. Exposes secrets to all tasks in the job, not just tasks that follow this one.",
"loc.messages.ClientIdCannotBeEmpty": "clientId must be a non empty string.",
"loc.messages.DomainCannotBeEmpty": "domain must be a non empty string.",
"loc.messages.SecretCannotBeEmpty": "secret must be a non empty string.",
"loc.messages.armUrlCannotBeEmpty": "arm URL must be a non empty string.",
"loc.messages.authorityUrlCannotBeEmpty": "authority must be a non empty string.",
"loc.messages.CallbackCannotBeNull": "callback cannot be null.",
"loc.messages.CredentialsCannotBeNull": "'credentials' cannot be null.",
"loc.messages.SubscriptionIdCannotBeNull": "'subscriptionId' cannot be null.",
"loc.messages.InvalidResponseLongRunningOperation": "Invalid response received for fetching status of a long running operation.",
"loc.messages.TimeoutWhileWaiting": "Timed out while waiting",
"loc.messages.ResourceGroupCannotBeNull": "resourceGroupName cannot be null or undefined and it must be of type string.",
"loc.messages.ResourceGroupExceededLength": "\"resourceGroupName\" should satisfy the constraint - \"MaxLength\": 90",
"loc.messages.ResourceGroupDeceededLength": "\"resourceGroupName\" should satisfy the constraint - \"MinLength\": 1",
"loc.messages.ResourceGroupDoesntMatchPattern": "\"resourceGroupName\" should satisfy the constraint - \"Pattern\": /^[-\\w\\._\\(\\)]+$/",
"loc.messages.AzKv_ConstructorFailed": "Azure key vault parameters initialization failed. Error: %s.",
"loc.messages.SubscriptionIdLabel": "SubscriptionId: %s.",
"loc.messages.KeyVaultNameLabel": "Key vault name: %s.",
"loc.messages.DownloadingSecretsUsing": "Downloading secrets using: %s.",
"loc.messages.GetSecretsFailed": "Get secrets failed. Error: %s.",
"loc.messages.NoSecretsFound": "No secrets found in: %s",
"loc.messages.NumberOfSecretsFound": "Number of secrets found in %s: %s",
"loc.messages.NumberOfEnabledSecretsFound": "Number of enabled and unexpired secrets found in %s: %s",
"loc.messages.DownloadingSecretValue": "Downloading secret value for: %s.",
"loc.messages.AccessDeniedError": "%s. The specified Azure service connection needs to have Get, List secret management permissions on the selected key vault. To set these permissions, download the ProvisionKeyVaultPermissions.ps1 script from build/release logs and execute it, or set them from the Azure portal.",
"loc.messages.GetSecretValueFailed": "Get secret value failed for: %s. Error: %s.",
"loc.messages.ConflictingVariableFound": "Variable with name %s is defined in both environment and key vault",
"loc.messages.GetSecretFailedBecauseOfInvalidCharacters": "Secret not found: %s. Secret name must be a string 1-127 characters in length containing only -, 0-9, a-z and A-Z.",
"loc.messages.UploadingAttachment": "Uploading %s as attachment",
"loc.messages.CouldNotWriteToFile": "Could not save content to file. Failed with an error %s",
"loc.messages.CouldNotMaskSecret": "%s value has regular expressions hence could not mask completely",
"loc.messages.CouldNotFetchAccessTokenforAzureStatusCode": "Could not fetch access token for Azure. Status code: %s, status message: %s",
"loc.messages.CouldNotFetchAccessTokenforMSIDueToMSINotConfiguredProperlyStatusCode": "Could not fetch access token for Managed Service Principal. Please configure Managed Service Identity (MSI) for virtual machine 'https://aka.ms/azure-msi-docs'. Status code: %s, status message: %s",
"loc.messages.CouldNotFetchAccessTokenforMSIStatusCode": "Could not fetch access token for Managed Service Principal. Status code: %s, status message: %s",
"loc.messages.RetryingWithVaultResourceIdFromResponse": "Retrying with vault resource Id reterieved from response : %s",
"loc.messages.ExpiredServicePrincipal": "Could not fetch access token for Azure. Verify if the Service Principal used is valid and not expired."
}
Loading