diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 7a9f1aaf031c..e7d430ceec54 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -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 diff --git a/Tasks/AzureKeyVaultV2/README.md b/Tasks/AzureKeyVaultV2/README.md new file mode 100644 index 000000000000..a01b1b58ea8a --- /dev/null +++ b/Tasks/AzureKeyVaultV2/README.md @@ -0,0 +1,77 @@ + + +# 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, [testuser@joehotmail.onmicrosoft.com](mailto:testuser@joehotmail.onmicrosoft.com) + - Login to the portal with this Active Directory account wiz. [testuser@joehotmail.onmicrosoft.com](mailto:testuser@joehotmail.onmicrosoft.com), 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 = + $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). diff --git a/Tasks/AzureKeyVaultV2/Strings/resources.resjson/de-de/resources.resjson b/Tasks/AzureKeyVaultV2/Strings/resources.resjson/de-de/resources.resjson new file mode 100644 index 000000000000..b9380d701d21 --- /dev/null +++ b/Tasks/AzureKeyVaultV2/Strings/resources.resjson/de-de/resources.resjson @@ -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." +} \ No newline at end of file diff --git a/Tasks/AzureKeyVaultV2/Strings/resources.resjson/en-US/resources.resjson b/Tasks/AzureKeyVaultV2/Strings/resources.resjson/en-US/resources.resjson new file mode 100644 index 000000000000..fa027a488f86 --- /dev/null +++ b/Tasks/AzureKeyVaultV2/Strings/resources.resjson/en-US/resources.resjson @@ -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:
 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." +} \ No newline at end of file diff --git a/Tasks/AzureKeyVaultV2/Strings/resources.resjson/es-es/resources.resjson b/Tasks/AzureKeyVaultV2/Strings/resources.resjson/es-es/resources.resjson new file mode 100644 index 000000000000..e4784e4aa5e1 --- /dev/null +++ b/Tasks/AzureKeyVaultV2/Strings/resources.resjson/es-es/resources.resjson @@ -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": "Descargar secretos de Azure Key Vault", + "loc.instanceNameFormat": "Instancia de Azure Key Vault: $(KeyVaultName)", + "loc.releaseNotes": "Funciona con los agentes multiplataforma (Linux, macOS o Windows)", + "loc.input.label.ConnectedServiceName": "Suscripción a Azure", + "loc.input.help.ConnectedServiceName": "Seleccione la suscripción de Azure para el almacén de claves", + "loc.input.label.KeyVaultName": "Almacén de claves", + "loc.input.help.KeyVaultName": "Proporcione el nombre de un almacén de claves existente", + "loc.input.label.SecretsFilter": "Filtro de secretos", + "loc.input.help.SecretsFilter": "Lista separada por comas de nombres de secretos. Use * para descargar todos los secretos del almacén de claves seleccionado.", + "loc.messages.ClientIdCannotBeEmpty": "clientId debe ser una cadena no vacía.", + "loc.messages.DomainCannotBeEmpty": "domain debe ser una cadena no vacía.", + "loc.messages.SecretCannotBeEmpty": "secret debe ser una cadena no vacía.", + "loc.messages.armUrlCannotBeEmpty": "La dirección URL de ARM debe ser una cadena no vacía.", + "loc.messages.authorityUrlCannotBeEmpty": "authority debe ser una cadena no vacía.", + "loc.messages.CallbackCannotBeNull": "callback no puede ser NULL.", + "loc.messages.CredentialsCannotBeNull": "\"credentials\" no puede ser NULL.", + "loc.messages.SubscriptionIdCannotBeNull": "\"subscriptionId\" no pude ser NULL.", + "loc.messages.InvalidResponseLongRunningOperation": "Se recibió una respuesta no válida para el estado de recuperación de una operación de ejecución prolongada.", + "loc.messages.TimeoutWhileWaiting": "Se agotó el tiempo de espera", + "loc.messages.ResourceGroupCannotBeNull": "resourceGroupName no puede ser NULL o no estar definido, y debe ser de tipo cadena.", + "loc.messages.ResourceGroupExceededLength": "\"resourceGroupName\" debe cumplir la restricción - \"MaxLength\": 90", + "loc.messages.ResourceGroupDeceededLength": "\"resourceGroupName\" debe cumplir la restricción - \"MinLength\": 1", + "loc.messages.ResourceGroupDoesntMatchPattern": "\"resourceGroupName\" debe cumplir la restricción - \"Patrón\": /^[-\\w\\._\\(\\)]+$/", + "loc.messages.AzKv_ConstructorFailed": "No se pudieron inicializar los parámetros de Azure Key Vault. Error: %s.", + "loc.messages.SubscriptionIdLabel": "Id. de suscripción: %s.", + "loc.messages.KeyVaultNameLabel": "Nombre del Key Vault: %s.", + "loc.messages.DownloadingSecretsUsing": "Descargando secretos con: %s.", + "loc.messages.GetSecretsFailed": "No se pudieron obtener los secretos. Error: %s.", + "loc.messages.NoSecretsFound": "No se han encontrado secretos en: %s", + "loc.messages.NumberOfSecretsFound": "Número de secretos encontrados en %s: %s", + "loc.messages.NumberOfEnabledSecretsFound": "Número de secretos habilitados y no expirados que se han encontrado en %s: %s", + "loc.messages.DownloadingSecretValue": "Descargando el valor del secreto para: %s.", + "loc.messages.AccessDeniedError": "%s. La conexión del servicio de Azure especificado debe tener permisos de administración de secretos de tipo Get y List en el almacén de claves seleccionado. Para configurar estos permisos, descargue el script ProvisionKeyVaultPermissions.ps1 desde los registros de compilación/versión y ejecútelo, o establézcalo en Azure Portal.", + "loc.messages.GetSecretValueFailed": "No se pudo obtener el valor del secreto para: %s. Error: %s.", + "loc.messages.ConflictingVariableFound": "La variable con el nombre %s está definida tanto en el entorno como en el almacén de claves", + "loc.messages.GetSecretFailedBecauseOfInvalidCharacters": "Secreto no encontrado: %s. El nombre secreto debe ser una cadena con una longitud de 1 a 127 caracteres que solo contenga -, 0-9, a-z y A-Z.", + "loc.messages.UploadingAttachment": "Cargando %s como datos adjuntos.", + "loc.messages.CouldNotWriteToFile": "No se pudo guardar contenido en el archivo. Error: %s", + "loc.messages.CouldNotMaskSecret": "El valor %s tiene expresiones regulares, de ahí que no se haya podido enmascarar completamente", + "loc.messages.CouldNotFetchAccessTokenforAzureStatusCode": "No se pudo capturar el token de acceso para Azure. Código de estado: %s. Mensaje de estado: %s", + "loc.messages.CouldNotFetchAccessTokenforMSIDueToMSINotConfiguredProperlyStatusCode": "No se pudo capturar el token de acceso para la entidad de servicio administrada. Configure Managed Service Identity (MSI) para la máquina virtual \"https://aka.ms/azure-msi-docs\". Código de estado: %s. Mensaje de estado: %s", + "loc.messages.CouldNotFetchAccessTokenforMSIStatusCode": "No se pudo capturar el token de acceso para la entidad de servicio administrada. Código de estado: %s. Mensaje de estado: %s", + "loc.messages.RetryingWithVaultResourceIdFromResponse": "Reintentando con el identificador de recurso de almacén recuperado de la respuesta: %s", + "loc.messages.ExpiredServicePrincipal": "No se pudo capturar el token de acceso de Azure. Compruebe que la entidad de servicio usada es válida y no ha expirado." +} \ No newline at end of file diff --git a/Tasks/AzureKeyVaultV2/Strings/resources.resjson/fr-fr/resources.resjson b/Tasks/AzureKeyVaultV2/Strings/resources.resjson/fr-fr/resources.resjson new file mode 100644 index 000000000000..169381f45e71 --- /dev/null +++ b/Tasks/AzureKeyVaultV2/Strings/resources.resjson/fr-fr/resources.resjson @@ -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": "Télécharger les secrets du coffre de clés Azure Key Vault", + "loc.instanceNameFormat": "Coffre de clés Azure : $(KeyVaultName)", + "loc.releaseNotes": "Fonctionne avec les agents multiplateformes (Linux, macOS ou Windows)", + "loc.input.label.ConnectedServiceName": "Abonnement Azure", + "loc.input.help.ConnectedServiceName": "Sélectionnez l'abonnement Azure du coffre de clés", + "loc.input.label.KeyVaultName": "Coffre de clés", + "loc.input.help.KeyVaultName": "Indiquez le nom d'un coffre de clés existant", + "loc.input.label.SecretsFilter": "Filtre de secrets", + "loc.input.help.SecretsFilter": "Liste de noms de secrets séparés par des virgules. Vous pouvez laisser * pour télécharger tous les secrets du coffre de clés sélectionné.", + "loc.messages.ClientIdCannotBeEmpty": "clientId doit être une chaîne non vide.", + "loc.messages.DomainCannotBeEmpty": "domain doit être une chaîne non vide.", + "loc.messages.SecretCannotBeEmpty": "secret doit être une chaîne non vide.", + "loc.messages.armUrlCannotBeEmpty": "L'URL arm doit être une chaîne non vide.", + "loc.messages.authorityUrlCannotBeEmpty": "authority doit être une chaîne non vide.", + "loc.messages.CallbackCannotBeNull": "callback ne peut pas avoir une valeur null.", + "loc.messages.CredentialsCannotBeNull": "'credentials' ne peut pas avoir une valeur null.", + "loc.messages.SubscriptionIdCannotBeNull": "'subscriptionId' ne peut pas avoir une valeur null.", + "loc.messages.InvalidResponseLongRunningOperation": "Réponse non valide reçue pour la récupération de l'état d'une opération longue.", + "loc.messages.TimeoutWhileWaiting": "Expiration du délai d'attente", + "loc.messages.ResourceGroupCannotBeNull": "resourceGroupName ne peut pas avoir une valeur null ou être non défini. De plus, il doit être de type chaîne.", + "loc.messages.ResourceGroupExceededLength": "\"resourceGroupName\" doit satisfaire la contrainte - \"MaxLength\" : 90", + "loc.messages.ResourceGroupDeceededLength": "\"resourceGroupName\" doit satisfaire la contrainte - \"MinLength\" : 1", + "loc.messages.ResourceGroupDoesntMatchPattern": "\"resourceGroupName\" doit satisfaire la contrainte - \"Pattern\" : /^[-\\w\\._\\(\\)]+$/", + "loc.messages.AzKv_ConstructorFailed": "L'initialisation des paramètres du coffre de clés Azure a échoué. Erreur : %s.", + "loc.messages.SubscriptionIdLabel": "SubscriptionId : %s.", + "loc.messages.KeyVaultNameLabel": "Nom du coffre de clés : %s.", + "loc.messages.DownloadingSecretsUsing": "Téléchargement des secrets à l'aide de %s.", + "loc.messages.GetSecretsFailed": "Échec de l'obtention des secrets. Erreur : %s.", + "loc.messages.NoSecretsFound": "Secrets introuvables dans %s", + "loc.messages.NumberOfSecretsFound": "Nombre de secrets trouvés dans %s : %s", + "loc.messages.NumberOfEnabledSecretsFound": "Nombre de secrets activés et non expirés trouvés dans %s : %s", + "loc.messages.DownloadingSecretValue": "Téléchargement de la valeur du secret pour %s.", + "loc.messages.AccessDeniedError": "%s. La connexion de service Azure spécifiée a besoin des autorisations de gestion de secret \"Obtenir, Lister\" pour le coffre de clés sélectionné. Pour définir ces autorisations, téléchargez le script ProvisionKeyVaultPermissions.ps1 à partir des journaux de build/mise en production, puis exécutez-le, ou définissez-le à partir du Portail Azure.", + "loc.messages.GetSecretValueFailed": "Échec de l'obtention de la valeur du secret pour %s. Erreur : %s.", + "loc.messages.ConflictingVariableFound": "La variable nommée %s est définie à la fois dans l'environnement et le coffre de clés", + "loc.messages.GetSecretFailedBecauseOfInvalidCharacters": "Secret introuvable : %s. Le nom doit être une chaîne comprise entre 1 et 127 caractères. Il doit contenir uniquement des tirets, des chiffres et des lettres : -, 0-9, a-z et A-Z.", + "loc.messages.UploadingAttachment": "Chargement de %s en tant que pièce jointe", + "loc.messages.CouldNotWriteToFile": "Impossible d'enregistrer le contenu dans le fichier. Échec avec une erreur %s", + "loc.messages.CouldNotMaskSecret": "La valeur %s comporte des expressions régulières, donc elle n'a pas pu être totalement masquée", + "loc.messages.CouldNotFetchAccessTokenforAzureStatusCode": "Impossible de récupérer (fetch) le jeton d'accès pour Azure. Code d'état : %s, message d'état : %s", + "loc.messages.CouldNotFetchAccessTokenforMSIDueToMSINotConfiguredProperlyStatusCode": "Impossible de récupérer (fetch) le jeton d'accès pour le principal du service managé. Configurez MSI (Managed Service Identity) pour la machine virtuelle 'https://aka.ms/azure-msi-docs'. Code d'état : %s, message d'état : %s", + "loc.messages.CouldNotFetchAccessTokenforMSIStatusCode": "Impossible de récupérer (fetch) le jeton d'accès pour le principal du service managé. Code d'état : %s, message d'état : %s", + "loc.messages.RetryingWithVaultResourceIdFromResponse": "Nouvelle tentative avec l'ID de ressource de coffre récupéré à partir de la réponse : %s", + "loc.messages.ExpiredServicePrincipal": "Impossible de récupérer (fetch) le jeton d'accès pour Azure. Vérifiez si le principal de service utilisé est valide et s'il n'a pas expiré." +} \ No newline at end of file diff --git a/Tasks/AzureKeyVaultV2/Strings/resources.resjson/it-IT/resources.resjson b/Tasks/AzureKeyVaultV2/Strings/resources.resjson/it-IT/resources.resjson new file mode 100644 index 000000000000..20c146f02a2e --- /dev/null +++ b/Tasks/AzureKeyVaultV2/Strings/resources.resjson/it-IT/resources.resjson @@ -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": "Scarica i segreti di Azure Key Vault", + "loc.instanceNameFormat": "Azure Key Vault: $(KeyVaultName)", + "loc.releaseNotes": "È compatibile con agenti multipiattaforma (Linux, macOS o Windows)", + "loc.input.label.ConnectedServiceName": "Sottoscrizione di Azure", + "loc.input.help.ConnectedServiceName": "Consente di selezionare la sottoscrizione di Azure per l'insieme di credenziali delle chiavi", + "loc.input.label.KeyVaultName": "Key Vault", + "loc.input.help.KeyVaultName": "Consente di specificare il nome di un insieme di credenziali delle chiavi esistente", + "loc.input.label.SecretsFilter": "Filtro segreti", + "loc.input.help.SecretsFilter": "Elenco di nomi di segreto delimitati da virgole. Lasciare impostato su * per scaricare tutti i segreti dal'insieme di credenziali delle chiavi selezionato.", + "loc.messages.ClientIdCannotBeEmpty": "clientId non deve essere una stringa vuota.", + "loc.messages.DomainCannotBeEmpty": "domain non deve essere una stringa vuota.", + "loc.messages.SecretCannotBeEmpty": "secret non deve essere una stringa vuota.", + "loc.messages.armUrlCannotBeEmpty": "L'URL di ARM non deve essere una stringa vuota.", + "loc.messages.authorityUrlCannotBeEmpty": "authority non deve essere una stringa vuota.", + "loc.messages.CallbackCannotBeNull": "il valore di callback non può essere Null.", + "loc.messages.CredentialsCannotBeNull": "Il valore di 'credentials' non può essere Null.", + "loc.messages.SubscriptionIdCannotBeNull": "Il valore di 'subscriptionId' non può essere Null.", + "loc.messages.InvalidResponseLongRunningOperation": "È stata ricevuta una risposta non valida per il recupero dello stato di un'operazione a esecuzione prolungata.", + "loc.messages.TimeoutWhileWaiting": "Si è verificato un timeout durante l'attesa", + "loc.messages.ResourceGroupCannotBeNull": "Il valore di resourceGroupName non può essere Null o non definito e deve essere di tipo stringa.", + "loc.messages.ResourceGroupExceededLength": "\"resourceGroupName\" deve soddisfare il vincolo - \"MaxLength\": 90", + "loc.messages.ResourceGroupDeceededLength": "\"resourceGroupName\" deve soddisfare il vincolo - \"MinLength\": 1", + "loc.messages.ResourceGroupDoesntMatchPattern": "\"resourceGroupName\" deve soddisfare il vincolo - \"Criterio\": /^[-\\w\\._\\(\\)]+$/", + "loc.messages.AzKv_ConstructorFailed": "L'inizializzazione dei parametri di Azure Key Vault non è riuscita. Errore: %s.", + "loc.messages.SubscriptionIdLabel": "ID sottoscrizione: %s.", + "loc.messages.KeyVaultNameLabel": "Nome del Key Vault: %s.", + "loc.messages.DownloadingSecretsUsing": "Download dei segreti con: %s.", + "loc.messages.GetSecretsFailed": "Recupero dei segreti non riuscito. Errore: %s.", + "loc.messages.NoSecretsFound": "Non sono stati trovati segreti in: %s", + "loc.messages.NumberOfSecretsFound": "Numero di segreti trovati in %s: %s", + "loc.messages.NumberOfEnabledSecretsFound": "Numero di segreti abilitati e non scaduti trovati in %s: %s", + "loc.messages.DownloadingSecretValue": "Download del valore del segreto per: %s.", + "loc.messages.AccessDeniedError": "%s. La connessione al servizio di Azure specificata deve disporre delle autorizzazioni Get e List di gestione segreti per l'insieme di credenziali delle chiavi selezionato. Per impostare queste autorizzazioni, scaricare lo script ProvisionKeyVaultPermissions.ps1 dai log di compilazione/versione ed eseguirlo oppure impostare le autorizzazioni dal portale di Azure.", + "loc.messages.GetSecretValueFailed": "Recupero del valore del segreto non riuscito per: %s. Errore: %s.", + "loc.messages.ConflictingVariableFound": "La variabile denominata %s è definita sia nell'ambiente che nell'insieme di credenziali delle chiavi", + "loc.messages.GetSecretFailedBecauseOfInvalidCharacters": "Il segreto %s non è stato trovato. Il nome del segreto deve essere una stringa composta da un minimo di uno a un massimo di 127 caratteri e contenente solo i caratteri -, 0-9, a-z e A-Z.", + "loc.messages.UploadingAttachment": "Caricamento di %s come allegato", + "loc.messages.CouldNotWriteToFile": "Non è stato possibile salvare il contenuto nel file. L'operazione non è riuscita. Errore: %s", + "loc.messages.CouldNotMaskSecret": "Il valore %s contiene espressioni regolari, di conseguenza non è stato possibile mascherarlo completamente", + "loc.messages.CouldNotFetchAccessTokenforAzureStatusCode": "Non è stato possibile recuperare il token di accesso per Azure. Codice di stato: %s. Messaggio di stato: %s", + "loc.messages.CouldNotFetchAccessTokenforMSIDueToMSINotConfiguredProperlyStatusCode": "Non è stato possibile recuperare il token di accesso per l'entità servizio gestita. Configurare l'identità del servizio gestita per la macchina virtuale 'https://aka.ms/azure-msi-docs'. Codice di stato: %s. Messaggio di stato: %s", + "loc.messages.CouldNotFetchAccessTokenforMSIStatusCode": "Non è stato possibile recuperare il token di accesso per l'entità servizio gestita. Codice di stato: %s. Messaggio di stato: %s", + "loc.messages.RetryingWithVaultResourceIdFromResponse": "Nuovo tentativo con l'ID risorsa dell'insieme di credenziali recuperato dalla risposta: %s", + "loc.messages.ExpiredServicePrincipal": "Non è stato possibile recuperare il token di accesso per Azure. Verificare che l'entità servizio usata sia valida e non sia scaduta." +} \ No newline at end of file diff --git a/Tasks/AzureKeyVaultV2/Strings/resources.resjson/ja-jp/resources.resjson b/Tasks/AzureKeyVaultV2/Strings/resources.resjson/ja-jp/resources.resjson new file mode 100644 index 000000000000..e2894bb2abb7 --- /dev/null +++ b/Tasks/AzureKeyVaultV2/Strings/resources.resjson/ja-jp/resources.resjson @@ -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 のシークレットをダウンロードします", + "loc.instanceNameFormat": "Azure Key Vault: $(KeyVaultName)", + "loc.releaseNotes": "クロスプラットフォーム エージェント (Linux、macOS、Windows) に対応", + "loc.input.label.ConnectedServiceName": "Azure サブスクリプション", + "loc.input.help.ConnectedServiceName": "キー コンテナー の Azure サブスクリプションを選択します", + "loc.input.label.KeyVaultName": "キー コンテナー", + "loc.input.help.KeyVaultName": "既存のキー コンテナーの名前を指定します", + "loc.input.label.SecretsFilter": "シークレットのフィルター", + "loc.input.help.SecretsFilter": "選択したキー コンテナーからダウンロードするシークレット名のコンマ区切りのリスト (すべてのシークレット名をダウンロードする場合は \"*\" のままにします)。", + "loc.messages.ClientIdCannotBeEmpty": "clientId は空ではない文字列にする必要があります。", + "loc.messages.DomainCannotBeEmpty": "domain を空の文字列にすることはできません。", + "loc.messages.SecretCannotBeEmpty": "secret を空の文字列にすることはできません。", + "loc.messages.armUrlCannotBeEmpty": "ARM URL を空の文字列にすることはできません。", + "loc.messages.authorityUrlCannotBeEmpty": "機関を空の文字列にすることはできません。", + "loc.messages.CallbackCannotBeNull": "コールバックを null にすることはできません。", + "loc.messages.CredentialsCannotBeNull": "'credentials' を null にすることはできません。", + "loc.messages.SubscriptionIdCannotBeNull": "'subscriptionId' を null にすることはできません。", + "loc.messages.InvalidResponseLongRunningOperation": "実行時間が長い操作の状態のフェッチに対して、無効な応答を受け取りました。", + "loc.messages.TimeoutWhileWaiting": "待機中にタイムアウトになりました", + "loc.messages.ResourceGroupCannotBeNull": "resourceGroupName を null または未定義にすることはできません。文字列型にする必要があります。", + "loc.messages.ResourceGroupExceededLength": "\"resourceGroupName\" は、次の制約を満たしている必要があります - \"MaxLength\": 90", + "loc.messages.ResourceGroupDeceededLength": "\"resourceGroupName\" は、次の制約を満たしている必要があります - \"MinLength\": 1", + "loc.messages.ResourceGroupDoesntMatchPattern": "\"resourceGroupName\" は、次の制約を満たしている必要があります - \"パターン\": /^[-\\w\\._\\(\\)]+$/", + "loc.messages.AzKv_ConstructorFailed": "Azure Key Vault パラメーターを初期化できませんでした。エラー: %s。", + "loc.messages.SubscriptionIdLabel": "サブスクリプション ID: %s。", + "loc.messages.KeyVaultNameLabel": "キー コンテナー名: %s。", + "loc.messages.DownloadingSecretsUsing": "%s を使ってシークレットをダウンロードしています。", + "loc.messages.GetSecretsFailed": "シークレットを取得できませんでした。エラー: %s。", + "loc.messages.NoSecretsFound": "%s にシークレットは見つかりません", + "loc.messages.NumberOfSecretsFound": "%s に見つかったシークレットの数: %s", + "loc.messages.NumberOfEnabledSecretsFound": "%s に見つかった、有効化されていて期限が切れていないシークレットの数: %s", + "loc.messages.DownloadingSecretValue": "%s のシークレット値をダウンロードしています。", + "loc.messages.AccessDeniedError": "%s。指定した Azure サービス接続には、選択したキー コンテナーに対するシークレット管理の取得と一覧のアクセス許可が必要です。これらのアクセス許可を設定するには、ビルド/リリースのログから ProvisionKeyVaultPermissions.ps1 スクリプトをダウンロードして実行するか、それらのアクセス許可を Azure portal から設定します。", + "loc.messages.GetSecretValueFailed": "%s のシークレット値を取得できませんでした。エラー: %s。", + "loc.messages.ConflictingVariableFound": "名前 %s の変数が、環境とキー コンテナーの両方で定義されています", + "loc.messages.GetSecretFailedBecauseOfInvalidCharacters": "シークレットが見つかりませんでした: %s。シークレット名は、-、0 から 9、a から z、A から Z のみを使った、長さが 1 から 127 文字の文字列である必要があります。", + "loc.messages.UploadingAttachment": "添付ファイルとして %s をアップロードしています", + "loc.messages.CouldNotWriteToFile": "ファイルにコンテンツを保存できませんでした。失敗しました。エラー: %s", + "loc.messages.CouldNotMaskSecret": "%s 値は正規表現を含むため、完全にマスクすることはできません", + "loc.messages.CouldNotFetchAccessTokenforAzureStatusCode": "Azure 用のアクセス トークンをフェッチできませんでした。状態コード: %s、状態メッセージ: %s", + "loc.messages.CouldNotFetchAccessTokenforMSIDueToMSINotConfiguredProperlyStatusCode": "マネージド サービス プリンシパルのアクセス トークンをフェッチできませんでした。仮想マシンのマネージド サービス ID (MSI) を構成してください 'https://aka.ms/azure-msi-docs'。状態コード: %s、ステータス メッセージ: %s", + "loc.messages.CouldNotFetchAccessTokenforMSIStatusCode": "マネージド サービス プリンシパルのアクセス トークンをフェッチできませんでした。状態コード: %s、ステータス メッセージ: %s", + "loc.messages.RetryingWithVaultResourceIdFromResponse": "応答から取得したコンテナー リソース ID で再試行しています: %s", + "loc.messages.ExpiredServicePrincipal": "Azure のアクセス トークンをフェッチできませんでした。使用されているサービス プリンシパルが有効であり、有効期限が切れていないことをご確認ください。" +} \ No newline at end of file diff --git a/Tasks/AzureKeyVaultV2/Strings/resources.resjson/ko-KR/resources.resjson b/Tasks/AzureKeyVaultV2/Strings/resources.resjson/ko-KR/resources.resjson new file mode 100644 index 000000000000..f8bad9f3c825 --- /dev/null +++ b/Tasks/AzureKeyVaultV2/Strings/resources.resjson/ko-KR/resources.resjson @@ -0,0 +1,48 @@ +{ + "loc.friendlyName": "Azure Key Vault", + "loc.helpMarkDown": "[이 작업에 대한 자세한 정보](https://go.microsoft.com/fwlink/?linkid=848891)", + "loc.description": "Azure Key Vault 비밀을 다운로드합니다.", + "loc.instanceNameFormat": "Azure Key Vault: $(KeyVaultName)", + "loc.releaseNotes": "플랫폼 간 에이전트(Linux, macOS 또는 Windows)에서 작동", + "loc.input.label.ConnectedServiceName": "Azure 구독", + "loc.input.help.ConnectedServiceName": "Key Vault에 대한 Azure 구독 선택", + "loc.input.label.KeyVaultName": "Key Vault", + "loc.input.help.KeyVaultName": "기존 Key Vault 이름 지정", + "loc.input.label.SecretsFilter": "비밀 필터", + "loc.input.help.SecretsFilter": "쉼표로 구분된 비밀 이름의 목록입니다. 또는 선택한 키 자격 증명 모음에서 모든 비밀을 다운로드하려면 *를 그대로 둡니다.", + "loc.messages.ClientIdCannotBeEmpty": "clientId는 비어 있지 않은 문자열이어야 합니다.", + "loc.messages.DomainCannotBeEmpty": "domain은 비어 있지 않은 문자열이어야 합니다.", + "loc.messages.SecretCannotBeEmpty": "secret은 비어 있지 않은 문자열이어야 합니다.", + "loc.messages.armUrlCannotBeEmpty": "arm URL은 비어 있지 않은 문자열이어야 합니다.", + "loc.messages.authorityUrlCannotBeEmpty": "authority는 비어 있지 않은 문자열이어야 합니다.", + "loc.messages.CallbackCannotBeNull": "호출은 null일 수 없습니다.", + "loc.messages.CredentialsCannotBeNull": "'credentials'는 null일 수 없습니다.", + "loc.messages.SubscriptionIdCannotBeNull": "'subscriptionId'는 null일 수 없습니다.", + "loc.messages.InvalidResponseLongRunningOperation": "오래 실행 중인 작업의 상태를 페치하기 위해 수신된 응답이 잘못되었습니다.", + "loc.messages.TimeoutWhileWaiting": "대기하는 동안 시간 초과됨", + "loc.messages.ResourceGroupCannotBeNull": "resourceGroupName은 null이거나 정의되지 않은 상태일 수 없으며, 문자열 형식이어야 합니다.", + "loc.messages.ResourceGroupExceededLength": "\"resourceGroupName\"은 제약 조건 \"MaxLength\": 90을 만족해야 합니다.", + "loc.messages.ResourceGroupDeceededLength": "\"resourceGroupName\"은 제약 조건 \"MinLength\": 1을 만족해야 합니다.", + "loc.messages.ResourceGroupDoesntMatchPattern": "\"resourceGroupName\"은 제약 조건 \"패턴\": /^[-\\w\\._\\(\\)]+$/를 만족해야 합니다.", + "loc.messages.AzKv_ConstructorFailed": "Azure Key Vault 매개 변수를 초기화하지 못했습니다. 오류: %s.", + "loc.messages.SubscriptionIdLabel": "SubscriptionId: %s.", + "loc.messages.KeyVaultNameLabel": "Key Vault 이름: %s.", + "loc.messages.DownloadingSecretsUsing": "%s을(를) 사용하여 비밀을 다운로드하는 중입니다.", + "loc.messages.GetSecretsFailed": "비밀을 가져오지 못했습니다. 오류: %s.", + "loc.messages.NoSecretsFound": "%s에서 비밀을 찾을 수 없습니다.", + "loc.messages.NumberOfSecretsFound": "%s에서 검색된 비밀의 수: %s", + "loc.messages.NumberOfEnabledSecretsFound": "%s에서 검색된 사용 가능하고 만료되지 않은 비밀의 수: %s", + "loc.messages.DownloadingSecretValue": "%s에 대한 비밀 값을 다운로드하는 중입니다.", + "loc.messages.AccessDeniedError": "%s. 지정한 Azure 서비스 연결에 선택한 키 자격 증명 모음에 대한 Get, List 비밀 관리 권한이 있어야 합니다. 이러한 권한을 설정하려면 빌드/릴리스 로그에서 ProvisionKeyVaultPermissions.ps1 스크립트를 다운로드하여 실행하거나 Azure Portal에서 설정하세요.", + "loc.messages.GetSecretValueFailed": "%s에 대한 비밀 값을 가져오지 못했습니다. 오류: %s.", + "loc.messages.ConflictingVariableFound": "이름이 %s인 변수가 환경과 Key Vault에서 모두 정의되었습니다.", + "loc.messages.GetSecretFailedBecauseOfInvalidCharacters": "비밀을 찾을 수 없습니다. %s. 비밀 이름은 -, 0-9, a-z, A-Z만 포함하는 1-127자 길이의 문자열이어야 합니다.", + "loc.messages.UploadingAttachment": "%s을(를) 첨부 파일로 업로드하는 중", + "loc.messages.CouldNotWriteToFile": "콘텐츠를 파일에 저장할 수 없습니다. %s 오류가 발생하여 실패했습니다.", + "loc.messages.CouldNotMaskSecret": "%s 값에 정규식이 있으므로 완전하게 마스크 처리할 수 없습니다.", + "loc.messages.CouldNotFetchAccessTokenforAzureStatusCode": "Azure에 대한 액세스 토큰을 페치할 수 없습니다. 상태 코드: %s, 상태 메시지: %s", + "loc.messages.CouldNotFetchAccessTokenforMSIDueToMSINotConfiguredProperlyStatusCode": "관리 서비스 주체에 대한 액세스 토큰을 페치할 수 없습니다. 가상 머신에 대한 MSI(관리 서비스 ID)를 구성하세요('https://aka.ms/azure-msi-docs'). 상태 코드: %s, 상태 메시지: %s", + "loc.messages.CouldNotFetchAccessTokenforMSIStatusCode": "관리 서비스 주체에 대한 액세스 토큰을 페치할 수 없습니다. 상태 코드: %s, 상태 메시지: %s", + "loc.messages.RetryingWithVaultResourceIdFromResponse": "응답에서 검색된 자격 증명 모음 리소스 ID를 사용하여 다시 시도하는 중: %s", + "loc.messages.ExpiredServicePrincipal": "Azure의 액세스 토큰을 페치할 수 없습니다. 사용한 서비스 주체가 유효하고 만료되지 않았는지 확인하세요." +} \ No newline at end of file diff --git a/Tasks/AzureKeyVaultV2/Strings/resources.resjson/ru-RU/resources.resjson b/Tasks/AzureKeyVaultV2/Strings/resources.resjson/ru-RU/resources.resjson new file mode 100644 index 000000000000..f92cfec67096 --- /dev/null +++ b/Tasks/AzureKeyVaultV2/Strings/resources.resjson/ru-RU/resources.resjson @@ -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", + "loc.instanceNameFormat": "Azure Key Vault: $(KeyVaultName)", + "loc.releaseNotes": "Поддержка кроссплатформенных агентов (Linux, macOS и Windows)", + "loc.input.label.ConnectedServiceName": "Подписка Azure", + "loc.input.help.ConnectedServiceName": "Выберите подписку Azure для Key Vault", + "loc.input.label.KeyVaultName": "Хранилище ключей", + "loc.input.help.KeyVaultName": "Укажите имя существующего хранилища ключей", + "loc.input.label.SecretsFilter": "Фильтр секретов", + "loc.input.help.SecretsFilter": "Укажите имена секретов, разделенные запятыми, или оставьте значение \"*\", чтобы скачать все секреты из выбранного хранилища ключей.", + "loc.messages.ClientIdCannotBeEmpty": "Параметр clientId должен быть непустой строкой.", + "loc.messages.DomainCannotBeEmpty": "Параметр domain должен быть непустой строкой.", + "loc.messages.SecretCannotBeEmpty": "Параметр secret должен быть непустой строкой.", + "loc.messages.armUrlCannotBeEmpty": "Параметр arm URL должен быть непустой строкой.", + "loc.messages.authorityUrlCannotBeEmpty": "Параметр authority должен быть непустой строкой.", + "loc.messages.CallbackCannotBeNull": "Обратный вызов не может иметь значение NULL.", + "loc.messages.CredentialsCannotBeNull": "Параметр credentials не может иметь значение NULL.", + "loc.messages.SubscriptionIdCannotBeNull": "Параметр \"subscriptionId\" не может иметь значение NULL.", + "loc.messages.InvalidResponseLongRunningOperation": "На запрос о получении состояния длительной операции получен недопустимый ответ.", + "loc.messages.TimeoutWhileWaiting": "Превышено время ожидания.", + "loc.messages.ResourceGroupCannotBeNull": "Параметр resourceGroupName не может иметь значение NULL или быть неопределенным. Также он должен быть строковым параметром.", + "loc.messages.ResourceGroupExceededLength": "Параметр \"resourceGroupName\" должен удовлетворять ограничению — \"MaxLength\": 90", + "loc.messages.ResourceGroupDeceededLength": "Параметр \"resourceGroupName\" должен удовлетворять ограничению — \"MinLength\": 1", + "loc.messages.ResourceGroupDoesntMatchPattern": "Параметр \"resourceGroupName\" должен удовлетворять ограничению — \"Шаблон\": /^[-\\w\\._\\(\\)]+$/", + "loc.messages.AzKv_ConstructorFailed": "Не удалось инициализировать параметры Azure Key Vault. Ошибка: %s.", + "loc.messages.SubscriptionIdLabel": "Идентификатор подписки: %s.", + "loc.messages.KeyVaultNameLabel": "Имя хранилища ключей: %s.", + "loc.messages.DownloadingSecretsUsing": "Скачивание секретов с помощью %s.", + "loc.messages.GetSecretsFailed": "Не удалось получить секреты. Ошибка: %s.", + "loc.messages.NoSecretsFound": "Секреты в %s не найдены", + "loc.messages.NumberOfSecretsFound": "Количество секретов в %s: %s", + "loc.messages.NumberOfEnabledSecretsFound": "Количество активных и действующих секретов в %s: %s", + "loc.messages.DownloadingSecretValue": "Скачивание значения секрета для %s.", + "loc.messages.AccessDeniedError": "%s. Указанное подключение к службе Azure должно иметь разрешения на управление секретами \"Получение\" и \"Вывод списка\" для выбранного хранилища ключей. Чтобы задать эти разрешения, скачайте скрипт ProvisionKeyVaultPermissions.ps1 из журналов сборки или выпуска и выполните его. Их также можно задать на портале Azure.", + "loc.messages.GetSecretValueFailed": "Не удалось получить значение секрета для %s. Ошибка: %s.", + "loc.messages.ConflictingVariableFound": "Переменная с именем %s определена как в окружении, так и в хранилище ключей", + "loc.messages.GetSecretFailedBecauseOfInvalidCharacters": "Секрет не найден: %s. Имя секрета должно быть строкой длиной от 1 до 127 символов, содержащей только цифры 0–9, буквы a–z, A–Z и символ -.", + "loc.messages.UploadingAttachment": "Идет отправка %s в качестве вложения", + "loc.messages.CouldNotWriteToFile": "Не удалось сохранить содержимое файла. Ошибка: %s", + "loc.messages.CouldNotMaskSecret": "Значение %s содержит регулярные выражения, поэтому полная маска невозможна", + "loc.messages.CouldNotFetchAccessTokenforAzureStatusCode": "Не удалось получить маркер доступа для Azure. Код состояния: %s, сообщение о состоянии: %s", + "loc.messages.CouldNotFetchAccessTokenforMSIDueToMSINotConfiguredProperlyStatusCode": "Не удалось получить маркер доступа для управляемого субъекта-службы. Настройте управляемое удостоверение службы (MSI) для виртуальной машины \"https://aka.ms/azure-msi-docs\". Код состояния: %s; сообщения о состоянии: %s.", + "loc.messages.CouldNotFetchAccessTokenforMSIStatusCode": "Не удалось получить маркер доступа для управляемого субъекта-службы. Код состояния: %s, сообщение о состоянии: %s.", + "loc.messages.RetryingWithVaultResourceIdFromResponse": "Повторная попытка с идентификатором ресурса хранилища, полученным из ответа: %s", + "loc.messages.ExpiredServicePrincipal": "Не удалось получить маркер доступа для Azure. Убедитесь, что используемый субъект-служба является допустимым, а срок его действия не истек." +} \ No newline at end of file diff --git a/Tasks/AzureKeyVaultV2/Strings/resources.resjson/zh-CN/resources.resjson b/Tasks/AzureKeyVaultV2/Strings/resources.resjson/zh-CN/resources.resjson new file mode 100644 index 000000000000..0570ab51d8b4 --- /dev/null +++ b/Tasks/AzureKeyVaultV2/Strings/resources.resjson/zh-CN/resources.resjson @@ -0,0 +1,48 @@ +{ + "loc.friendlyName": "Azure Key Vault", + "loc.helpMarkDown": "[详细了解此任务](https://go.microsoft.com/fwlink/?linkid=848891)", + "loc.description": "下载 Azure Key Vault 密钥", + "loc.instanceNameFormat": "Azure Key Vault: $(KeyVaultName)", + "loc.releaseNotes": "适用于跨平台代理(Linux、macOS 或 Windows)", + "loc.input.label.ConnectedServiceName": "Azure 订阅", + "loc.input.help.ConnectedServiceName": "为密钥保管库选择 Azure 订阅", + "loc.input.label.KeyVaultName": "密钥保管库", + "loc.input.help.KeyVaultName": "提供现有密钥保管库的名称", + "loc.input.label.SecretsFilter": "密钥筛选器", + "loc.input.help.SecretsFilter": "用逗号分隔的密钥名称列表或保留 * 以下载所选密钥保管库中的所有密钥。", + "loc.messages.ClientIdCannotBeEmpty": "clientId 必需是非空字符串。", + "loc.messages.DomainCannotBeEmpty": "domain 必须为非空字符串。", + "loc.messages.SecretCannotBeEmpty": "secret 必须是非空字符串。", + "loc.messages.armUrlCannotBeEmpty": "arm URL 必须是非空字符串。", + "loc.messages.authorityUrlCannotBeEmpty": "authority 必须是非空字符串。", + "loc.messages.CallbackCannotBeNull": "回叫不能为 NULL。", + "loc.messages.CredentialsCannotBeNull": "\"credentials\" 不能为 null。", + "loc.messages.SubscriptionIdCannotBeNull": "\"subscriptionId\" 不能为 NULL。", + "loc.messages.InvalidResponseLongRunningOperation": "提取长时间运行操作的状态时收到无效的响应。", + "loc.messages.TimeoutWhileWaiting": "等待超时", + "loc.messages.ResourceGroupCannotBeNull": "resourceGroupName 不能为 NULL 或未定义,且必须为类型字符串。", + "loc.messages.ResourceGroupExceededLength": "\"resourceGroupName\" 应满足约束 - \"MaxLength\": 90", + "loc.messages.ResourceGroupDeceededLength": "\"resourceGroupName\" 应满足约束 - \"MinLength\": 1", + "loc.messages.ResourceGroupDoesntMatchPattern": "\"resourceGroupName\" 应满足约束 -“模式”: /^[-\\w\\._\\(\\)]+$/", + "loc.messages.AzKv_ConstructorFailed": "Azure Key Vault 参数初始化失败。错误: %s。", + "loc.messages.SubscriptionIdLabel": "SubscriptionId: %s。", + "loc.messages.KeyVaultNameLabel": "密钥保管库名称: %s。", + "loc.messages.DownloadingSecretsUsing": "正在使用 %s 下载密钥。", + "loc.messages.GetSecretsFailed": "获取密钥失败。错误: %s。", + "loc.messages.NoSecretsFound": "未在 %s 中找到密钥", + "loc.messages.NumberOfSecretsFound": "在 %s 中找到的密钥数: %s", + "loc.messages.NumberOfEnabledSecretsFound": "在 %s 中启用且未过期的密钥数: %s", + "loc.messages.DownloadingSecretValue": "正在下载 %s 的密钥值。", + "loc.messages.AccessDeniedError": "%s。指定的 Azure 服务连接需要具有所选密钥库的 Get、List 密钥管理权限。要设置这些权限,请从生成/发布日志下载 ProvisionKeyVaultPermissions.ps1 脚本并执行,或者从 Azure 门户设置这些权限。", + "loc.messages.GetSecretValueFailed": "获取 %s 的密钥值失败。错误: %s。", + "loc.messages.ConflictingVariableFound": "在环境和密钥保管库中同时定义名为 %s 的变量", + "loc.messages.GetSecretFailedBecauseOfInvalidCharacters": "找不到密钥: %s。密钥名称必须是长度为 1-127 个字符且仅包含 - 、0-9、a-z 和 A-Z 的字符串。", + "loc.messages.UploadingAttachment": "正在将 %s 作为附件上传", + "loc.messages.CouldNotWriteToFile": "无法将内容保存至文件。失败,并出现错误 %s", + "loc.messages.CouldNotMaskSecret": "%s 值具有正则表达式,因此可能不会完全遮蔽", + "loc.messages.CouldNotFetchAccessTokenforAzureStatusCode": "无法提取 Azure 的访问令牌。状态代码: %s,状态消息: %s", + "loc.messages.CouldNotFetchAccessTokenforMSIDueToMSINotConfiguredProperlyStatusCode": "无法提取托管服务主体的访问令牌。请为虚拟机配置托管服务标识(MSI)(https://aka.ms/azure-msi-docs)。状态代码: %s,状态消息: %s", + "loc.messages.CouldNotFetchAccessTokenforMSIStatusCode": "无法提取托管服务主体的访问令牌。状态代码: %s,状态消息: %s", + "loc.messages.RetryingWithVaultResourceIdFromResponse": "使用从响应中检索到的保管库资源 ID 进行重试: %s", + "loc.messages.ExpiredServicePrincipal": "无法提取 Azure 的访问令牌。请确保使用的服务主体有效且未过期。" +} \ No newline at end of file diff --git a/Tasks/AzureKeyVaultV2/Strings/resources.resjson/zh-TW/resources.resjson b/Tasks/AzureKeyVaultV2/Strings/resources.resjson/zh-TW/resources.resjson new file mode 100644 index 000000000000..c2303ac964a1 --- /dev/null +++ b/Tasks/AzureKeyVaultV2/Strings/resources.resjson/zh-TW/resources.resjson @@ -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 祕密", + "loc.instanceNameFormat": "Azure Key Vault: $(KeyVaultName)", + "loc.releaseNotes": "適用於跨平台代理程式 (Linux、macOS 或 Windows)", + "loc.input.label.ConnectedServiceName": "Azure 訂用帳戶", + "loc.input.help.ConnectedServiceName": "為金鑰保存庫選取 Azure 訂用帳戶", + "loc.input.label.KeyVaultName": "金鑰保存庫", + "loc.input.help.KeyVaultName": "提供現有金鑰保存庫的名稱", + "loc.input.label.SecretsFilter": "祕密篩選", + "loc.input.help.SecretsFilter": "逗點分隔的祕密名稱清單,或保留 * 從選取的金鑰保存庫下載所有祕密。", + "loc.messages.ClientIdCannotBeEmpty": "clientId 不得為空字串。", + "loc.messages.DomainCannotBeEmpty": "domain 不得為空字串。", + "loc.messages.SecretCannotBeEmpty": "secret 不得為空字串。", + "loc.messages.armUrlCannotBeEmpty": "arm URL 不得為空字串。", + "loc.messages.authorityUrlCannotBeEmpty": "authority 不得為空字串。", + "loc.messages.CallbackCannotBeNull": "回撥不得為 null。", + "loc.messages.CredentialsCannotBeNull": "'credentials' 不得為 null。", + "loc.messages.SubscriptionIdCannotBeNull": "'subscriptionId' 不得為 null。", + "loc.messages.InvalidResponseLongRunningOperation": "擷取執行時間長之作業的狀態時,收到無效的回應。", + "loc.messages.TimeoutWhileWaiting": "等候時發生逾時", + "loc.messages.ResourceGroupCannotBeNull": "resourceGroupName 不得為 null 或未定義。其必須是 type 字串。", + "loc.messages.ResourceGroupExceededLength": "\"resourceGroupName\" 必須滿足條件約束 - \"MaxLength\": 90", + "loc.messages.ResourceGroupDeceededLength": "\"resourceGroupName\" 必須滿足條件約束 - \"MinLength\": 1", + "loc.messages.ResourceGroupDoesntMatchPattern": "\"resourceGroupName\" 必須滿足限制式 -「模式」: /^[-\\w\\._\\(\\)]+$/", + "loc.messages.AzKv_ConstructorFailed": "Azure Key Vault 參數初始化失敗。錯誤: %s。", + "loc.messages.SubscriptionIdLabel": "SubscriptionId: %s。", + "loc.messages.KeyVaultNameLabel": "金鑰保存庫名稱: %s。", + "loc.messages.DownloadingSecretsUsing": "正在使用 %s 下載祕密。", + "loc.messages.GetSecretsFailed": "取得祕密失敗。錯誤: %s。", + "loc.messages.NoSecretsFound": "在 %s 中找不到任何祕密", + "loc.messages.NumberOfSecretsFound": "在 %s 中找到的祕密數: %s", + "loc.messages.NumberOfEnabledSecretsFound": "在 %s 中找到已啟用且未到期的祕密數: %s", + "loc.messages.DownloadingSecretValue": "正在下載 %s 的祕密值。", + "loc.messages.AccessDeniedError": "%s。指定的 Azure 服務連線必須有所選金鑰保存庫上的 Get、List 祕密管理權限。若要設定這些權限,請從組建/版本記錄檔下載 ProvisionKeyVaultPermissions.ps1 指令碼並加以執行,也可從 Azure 入口網站加以設定。", + "loc.messages.GetSecretValueFailed": "無法取得 %s 的祕密值。錯誤: %s。", + "loc.messages.ConflictingVariableFound": "環境和金鑰保存庫中都定義了名稱為 %s 的變數", + "loc.messages.GetSecretFailedBecauseOfInvalidCharacters": "找不到祕密: %s。名稱必須是長度為 1-127 個字元的字串,而且只可包含 -、0-9、a-z 和 A-Z。", + "loc.messages.UploadingAttachment": "正在將 %s 上傳為附件", + "loc.messages.CouldNotWriteToFile": "無法將內容儲存至檔案。失敗,發生錯誤: %s", + "loc.messages.CouldNotMaskSecret": "因而無法完全遮罩 %s 值的規則運算式", + "loc.messages.CouldNotFetchAccessTokenforAzureStatusCode": "無法擷取 Azure 的存取權杖。狀態碼: %s,狀態訊息: %s", + "loc.messages.CouldNotFetchAccessTokenforMSIDueToMSINotConfiguredProperlyStatusCode": "無法擷取受控服務主體的存取權杖。請設定虛擬機器的受控服務識別 (MSI) (https://aka.ms/azure-msi-docs)。狀態碼: %s,狀態訊息: %s", + "loc.messages.CouldNotFetchAccessTokenforMSIStatusCode": "無法擷取受控服務主體的存取權杖。狀態碼: %s,狀態訊息: %s", + "loc.messages.RetryingWithVaultResourceIdFromResponse": "正在以從回應擷取的保存庫資源識別碼重試 : %s", + "loc.messages.ExpiredServicePrincipal": "無法擷取 Azure 的存取權杖。請驗證使用的服務主體是否有效且未過期。" +} \ No newline at end of file diff --git a/Tasks/AzureKeyVaultV2/Tests/L0.ts b/Tasks/AzureKeyVaultV2/Tests/L0.ts new file mode 100644 index 000000000000..d67c10aa04aa --- /dev/null +++ b/Tasks/AzureKeyVaultV2/Tests/L0.ts @@ -0,0 +1,116 @@ +/// +'use strict'; + +const assert = require('assert'); +const ttm = require('azure-pipelines-task-lib/mock-test'); +const path = require('path'); + +describe('Azure Key Vault', function () { + this.timeout(30000); + before((done) => { + done(); + }); + after(function () { + }); + it("Successfully download all secrets", (done) => { + let tp = path.join(__dirname, "downloadAllSecrets.js"); + let tr = new ttm.MockTestRunner(tp); + + tr.run(); + try { + assert(tr.succeeded, "Should have succeeded"); + assert(tr.stdout.indexOf("KeyVaultNameLabel RmCdpKeyVault") > 0, "KeyVaultNameLabel RmCdpKeyVault"); + assert(tr.stdout.indexOf("set SYSTEM_UNSAFEALLOWMULTILINESECRET=true") > 0, "set SYSTEM_UNSAFEALLOWMULTILINESECRET=true"); + assert(tr.stdout.indexOf("Downloading all secrets from subscriptionId: sId, vault: RmCdpKeyVault") > 0, "Downloading all secrets from subscriptionId: sId, vault: RmCdpKeyVault"); + assert(tr.stdout.indexOf("keyVaultClient.getSecrets is called") > 0, "keyVaultClient.getSecrets is called"); + assert(tr.stdout.indexOf("NumberOfSecretsFound RmCdpKeyVault 5") > 0, "NumberOfSecretsFound RmCdpKeyVault 5"); + assert(tr.stdout.indexOf("NumberOfEnabledSecretsFound RmCdpKeyVault 4") > 0, "NumberOfEnabledSecretsFound RmCdpKeyVault 4"); + assert(tr.stdout.indexOf("getSecretValue is called for secret1") > 0, "getSecretValue is called for secret1"); + assert(tr.stdout.indexOf("getSecretValue is called for secret2") > 0, "getSecretValue is called for secret2"); + assert(tr.stdout.indexOf("getSecretValue is called for secret3") > 0, "getSecretValue is called for secret3"); + + assert(tr.stdout.indexOf("getSecretValue is called for secret4") < 0, "getSecretValue should not be called for secret4"); + + assert(tr.stdout.indexOf("##vso[task.setvariable variable=secret1;issecret=true;]secret1-value") > 0, "##vso[task.setvariable variable=secret1;issecret=true;]secret1-value"); + assert(tr.stdout.indexOf("##vso[task.setvariable variable=secret2;issecret=true;]secret2-value") > 0, "##vso[task.setvariable variable=secret2;issecret=true;]secret2-value"); + assert(tr.stdout.indexOf("##vso[task.setvariable variable=secret3;issecret=true;]secret3-value") > 0, "##vso[task.setvariable variable=secret3;issecret=true;]secret3-value"); + assert(tr.stdout.indexOf("##vso[task.setvariable variable=secret5_%AZP253B;issecret=true;]secret5_%AZP253B-value") > 0, "##vso[task.setvariable variable=secret5_%AZP253B;issecret=true;]secret5_%AZP253B-value"); + + assert(tr.stdout.indexOf("##vso[task.setvariable variable=secret4;issecret=true;]secret4-value") < 0, "secret4 value should not be set"); + + done(); + } + catch (error) { + console.log("STDERR", tr.stderr); + console.log("STDOUT", tr.stdout); + done(error); + } + }); + it("Successfully download selected secrets", (done) => { + let tp = path.join(__dirname, "downloadSelectedSecrets.js"); + let tr = new ttm.MockTestRunner(tp); + + tr.run(); + try { + assert(tr.succeeded, "Should have succeeded"); + assert(tr.stdout.indexOf("KeyVaultNameLabel RmCdpKeyVault") > 0, "KeyVaultNameLabel RmCdpKeyVault"); + assert(tr.stdout.indexOf("set SYSTEM_UNSAFEALLOWMULTILINESECRET=true") > 0, "set SYSTEM_UNSAFEALLOWMULTILINESECRET=true"); + assert(tr.stdout.indexOf("Downloading all secrets from subscriptionId: sId, vault: RmCdpKeyVault") < 0, "Should not downloading all secrets"); + assert(tr.stdout.indexOf("keyVaultClient.getSecrets is called") < 0, "keyVaultClient.getSecrets should not be called"); + assert(tr.stdout.indexOf("NumberOfSecretsFound RmCdpKeyVault") < 0, "NumberOfSecretsFound RmCdpKeyVault should not be there"); + + assert(tr.stdout.indexOf("getSecretValue is called for secret1") > 0, "getSecretValue is called for secret1"); + assert(tr.stdout.indexOf("getSecretValue is called for secret2") > 0, "getSecretValue is called for secret2"); + assert(tr.stdout.indexOf("getSecretValue is called for secret3/versionIdentifierGuid") > 0, "getSecretValue is called for secret3/versionIdentifierGuid"); + + assert(tr.stdout.indexOf("##vso[task.setvariable variable=secret1;issecret=true;]secret1-value") > 0, "##vso[task.setvariable variable=secret1;issecret=true;]secret1-value"); + assert(tr.stdout.indexOf("##vso[task.setvariable variable=secret2;issecret=true;]secret2-value") > 0, "##vso[task.setvariable variable=secret2;issecret=true;]secret2-value"); + assert(tr.stdout.indexOf("##vso[task.setvariable variable=secret3;issecret=true;]secret3/versionIdentifierGuid-value") > 0, "##vso[task.setvariable variable=secret3;issecret=true;]secret3/versionIdentifierGuid-value"); + assert(tr.stdout.indexOf("##vso[task.setvariable variable=secret3/versionIdentifierGuid;issecret=true;]secret3/versionIdentifierGuid-value") > 0, "##vso[task.setvariable variable=secret3/versionIdentifierGuid;issecret=true;]secret3/versionIdentifierGuid-value"); + assert(tr.stdout.indexOf("##vso[task.setvariable variable=secret5_%AZP253B;issecret=true;]secret5_%AZP253B-value") > 0, "##vso[task.setvariable variable=secret5_%AZP253B;issecret=true;]secret5_%AZP253B-value"); + + assert(tr.stdout.indexOf("##vso[task.setvariable variable=secret4;issecret=true;]secret4-value") < 0, "secret4 value should not be set"); + + done(); + } + catch (error) { + console.log("STDERR", tr.stderr); + console.log("STDOUT", tr.stdout); + done(error); + } + }); + it("Task fails if key vault name is not specified", (done) => { + let tp = path.join(__dirname, "downloadSecretsWithoutKeyVault.js"); + let tr = new ttm.MockTestRunner(tp); + + tr.run(); + try { + assert(tr.failed, "Should have failed"); + assert(tr.stdout.indexOf("Error: Input required: KeyVaultName") > 0, "Error: Input required: KeyVaultName"); + + done(); + } + catch (error) { + console.log("STDERR", tr.stderr); + console.log("STDOUT", tr.stdout); + done(error); + } + }); + it("Task fails if secret filter is not specified", (done) => { + let tp = path.join(__dirname, "downloadSecretsWithoutSecrets.js"); + let tr = new ttm.MockTestRunner(tp); + + tr.run(); + try { + assert(tr.failed, "Should have failed"); + assert(tr.stdout.indexOf("Error: Input required: SecretsFilter") > 0, "Error: Input required: SecretsFilter"); + + done(); + } + catch (error) { + console.log("STDERR", tr.stderr); + console.log("STDOUT", tr.stdout); + done(error); + } + }); +}); diff --git a/Tasks/AzureKeyVaultV2/Tests/downloadAllSecrets.ts b/Tasks/AzureKeyVaultV2/Tests/downloadAllSecrets.ts new file mode 100644 index 000000000000..68b96296193d --- /dev/null +++ b/Tasks/AzureKeyVaultV2/Tests/downloadAllSecrets.ts @@ -0,0 +1,31 @@ +import ma = require('azure-pipelines-task-lib/mock-answer'); +import tmrm = require('azure-pipelines-task-lib/mock-run'); +import path = require('path'); + +let taskPath = path.join(__dirname, '..', 'run.js'); +let tr: tmrm.TaskMockRunner = new tmrm.TaskMockRunner(taskPath); + +tr.setInput("ConnectedServiceName", "AzureRMSpn"); +tr.setInput("KeyVaultName", "RmCdpKeyVault"); +tr.setInput("SecretsFilter", "*"); +tr.setInput("RunAsPreJob", "false"); + +process.env["ENDPOINT_AUTH_SCHEME_AzureRMSpn"] = "ServicePrincipal"; +process.env["ENDPOINT_AUTH_PARAMETER_AzureRMSpn_SERVICEPRINCIPALID"] = "spId"; +process.env["ENDPOINT_AUTH_PARAMETER_AzureRMSpn_SERVICEPRINCIPALKEY"] = "spKey"; +process.env["ENDPOINT_AUTH_PARAMETER_AzureRMSpn_TENANTID"] = "tenant"; +process.env["ENDPOINT_DATA_AzureRMSpn_SUBSCRIPTIONNAME"] = "sName"; +process.env["ENDPOINT_DATA_AzureRMSpn_SUBSCRIPTIONID"] = "sId"; +process.env["ENDPOINT_DATA_AzureRMSpn_SPNOBJECTID"] = "oId"; +process.env["ENDPOINT_DATA_AzureRMSpn_ENVIRONMENTAUTHORITYURL"] = "https://login.windows.net/"; +process.env["ENDPOINT_DATA_AzureRMSpn_ACTIVEDIRECTORYSERVICEENDPOINTRESOURCEID"] = "https://login.windows.net/"; +process.env["ENDPOINT_DATA_AzureRMSpn_GRAPHURL"] = "https://graph.windows.net/"; +process.env["ENDPOINT_DATA_AzureRMSpn_AzureKeyVaultServiceEndpointResourceId"] = "https://vault.azure.net"; +process.env["ENDPOINT_URL_AzureRMSpn"] = "https://management.azure.com/"; +process.env["SYSTEM_DEFAULTWORKINGDIRECTORY"] = "C:\\a\\w\\"; +process.env["AGENT_TEMPDIRECTORY"] = process.cwd(); + +tr.registerMock('azure-pipelines-task-lib/toolrunner', require('azure-pipelines-task-lib/mock-toolrunner')); +tr.registerMock('./azure-arm-keyvault', require('./mock_node_modules/azure-arm-keyvault')); + +tr.run(); \ No newline at end of file diff --git a/Tasks/AzureKeyVaultV2/Tests/downloadSecretsWithoutKeyVault.ts b/Tasks/AzureKeyVaultV2/Tests/downloadSecretsWithoutKeyVault.ts new file mode 100644 index 000000000000..baa04831ac10 --- /dev/null +++ b/Tasks/AzureKeyVaultV2/Tests/downloadSecretsWithoutKeyVault.ts @@ -0,0 +1,30 @@ +import ma = require('azure-pipelines-task-lib/mock-answer'); +import tmrm = require('azure-pipelines-task-lib/mock-run'); +import path = require('path'); + +let taskPath = path.join(__dirname, '..', 'run.js'); +let tr: tmrm.TaskMockRunner = new tmrm.TaskMockRunner(taskPath); + +tr.setInput("ConnectedServiceName", "AzureRMSpn"); +tr.setInput("SecretsFilter", "secret1, secret2 "); +tr.setInput("RunAsPreJob", "false"); + +process.env["ENDPOINT_AUTH_SCHEME_AzureRMSpn"] = "ServicePrincipal"; +process.env["ENDPOINT_AUTH_PARAMETER_AzureRMSpn_SERVICEPRINCIPALID"] = "spId"; +process.env["ENDPOINT_AUTH_PARAMETER_AzureRMSpn_SERVICEPRINCIPALKEY"] = "spKey"; +process.env["ENDPOINT_AUTH_PARAMETER_AzureRMSpn_TENANTID"] = "tenant"; +process.env["ENDPOINT_DATA_AzureRMSpn_SUBSCRIPTIONNAME"] = "sName"; +process.env["ENDPOINT_DATA_AzureRMSpn_SUBSCRIPTIONID"] = "sId"; +process.env["ENDPOINT_DATA_AzureRMSpn_SPNOBJECTID"] = "oId"; +process.env["ENDPOINT_DATA_AzureRMSpn_ENVIRONMENTAUTHORITYURL"] = "https://login.windows.net/"; +process.env["ENDPOINT_DATA_AzureRMSpn_ACTIVEDIRECTORYSERVICEENDPOINTRESOURCEID"] = "https://login.windows.net/"; +process.env["ENDPOINT_DATA_AzureRMSpn_GRAPHURL"] = "https://graph.windows.net/"; +process.env["ENDPOINT_DATA_AzureRMSpn_AzureKeyVaultServiceEndpointResourceId"] = "https://vault.azure.net"; +process.env["ENDPOINT_URL_AzureRMSpn"] = "https://management.azure.com/"; +process.env["SYSTEM_DEFAULTWORKINGDIRECTORY"] = "C:\\a\\w\\"; +process.env["AGENT_TEMPDIRECTORY"] = process.cwd(); + +tr.registerMock('azure-pipelines-task-lib/toolrunner', require('azure-pipelines-task-lib/mock-toolrunner')); +tr.registerMock('./azure-arm-keyvault', require('./mock_node_modules/azure-arm-keyvault')); + +tr.run(); \ No newline at end of file diff --git a/Tasks/AzureKeyVaultV2/Tests/downloadSecretsWithoutSecrets.ts b/Tasks/AzureKeyVaultV2/Tests/downloadSecretsWithoutSecrets.ts new file mode 100644 index 000000000000..615ddf7e716c --- /dev/null +++ b/Tasks/AzureKeyVaultV2/Tests/downloadSecretsWithoutSecrets.ts @@ -0,0 +1,30 @@ +import ma = require('azure-pipelines-task-lib/mock-answer'); +import tmrm = require('azure-pipelines-task-lib/mock-run'); +import path = require('path'); + +let taskPath = path.join(__dirname, '..', 'run.js'); +let tr: tmrm.TaskMockRunner = new tmrm.TaskMockRunner(taskPath); + +tr.setInput("ConnectedServiceName", "AzureRMSpn"); +tr.setInput("KeyVaultName", "RmCdpKeyVault"); +tr.setInput("RunAsPreJob", "false"); + +process.env["ENDPOINT_AUTH_SCHEME_AzureRMSpn"] = "ServicePrincipal"; +process.env["ENDPOINT_AUTH_PARAMETER_AzureRMSpn_SERVICEPRINCIPALID"] = "spId"; +process.env["ENDPOINT_AUTH_PARAMETER_AzureRMSpn_SERVICEPRINCIPALKEY"] = "spKey"; +process.env["ENDPOINT_AUTH_PARAMETER_AzureRMSpn_TENANTID"] = "tenant"; +process.env["ENDPOINT_DATA_AzureRMSpn_SUBSCRIPTIONNAME"] = "sName"; +process.env["ENDPOINT_DATA_AzureRMSpn_SUBSCRIPTIONID"] = "sId"; +process.env["ENDPOINT_DATA_AzureRMSpn_SPNOBJECTID"] = "oId"; +process.env["ENDPOINT_DATA_AzureRMSpn_ENVIRONMENTAUTHORITYURL"] = "https://login.windows.net/"; +process.env["ENDPOINT_DATA_AzureRMSpn_ACTIVEDIRECTORYSERVICEENDPOINTRESOURCEID"] = "https://login.windows.net/"; +process.env["ENDPOINT_DATA_AzureRMSpn_GRAPHURL"] = "https://graph.windows.net/"; +process.env["ENDPOINT_DATA_AzureRMSpn_AzureKeyVaultServiceEndpointResourceId"] = "https://vault.azure.net"; +process.env["ENDPOINT_URL_AzureRMSpn"] = "https://management.azure.com/"; +process.env["SYSTEM_DEFAULTWORKINGDIRECTORY"] = "C:\\a\\w\\"; +process.env["AGENT_TEMPDIRECTORY"] = process.cwd(); + +tr.registerMock('azure-pipelines-task-lib/toolrunner', require('azure-pipelines-task-lib/mock-toolrunner')); +tr.registerMock('./azure-arm-keyvault', require('./mock_node_modules/azure-arm-keyvault')); + +tr.run(); \ No newline at end of file diff --git a/Tasks/AzureKeyVaultV2/Tests/downloadSelectedSecrets.ts b/Tasks/AzureKeyVaultV2/Tests/downloadSelectedSecrets.ts new file mode 100644 index 000000000000..bdfc54f7c210 --- /dev/null +++ b/Tasks/AzureKeyVaultV2/Tests/downloadSelectedSecrets.ts @@ -0,0 +1,31 @@ +import ma = require('azure-pipelines-task-lib/mock-answer'); +import tmrm = require('azure-pipelines-task-lib/mock-run'); +import path = require('path'); + +let taskPath = path.join(__dirname, '..', 'run.js'); +let tr: tmrm.TaskMockRunner = new tmrm.TaskMockRunner(taskPath); + +tr.setInput("ConnectedServiceName", "AzureRMSpn"); +tr.setInput("KeyVaultName", "RmCdpKeyVault"); +tr.setInput("SecretsFilter", "secret1, secret2, secret3/versionIdentifierGuid, secret5_%3B "); +tr.setInput("RunAsPreJob", "false"); + +process.env["ENDPOINT_AUTH_SCHEME_AzureRMSpn"] = "ServicePrincipal"; +process.env["ENDPOINT_AUTH_PARAMETER_AzureRMSpn_SERVICEPRINCIPALID"] = "spId"; +process.env["ENDPOINT_AUTH_PARAMETER_AzureRMSpn_SERVICEPRINCIPALKEY"] = "spKey"; +process.env["ENDPOINT_AUTH_PARAMETER_AzureRMSpn_TENANTID"] = "tenant"; +process.env["ENDPOINT_DATA_AzureRMSpn_SUBSCRIPTIONNAME"] = "sName"; +process.env["ENDPOINT_DATA_AzureRMSpn_SUBSCRIPTIONID"] = "sId"; +process.env["ENDPOINT_DATA_AzureRMSpn_SPNOBJECTID"] = "oId"; +process.env["ENDPOINT_DATA_AzureRMSpn_ENVIRONMENTAUTHORITYURL"] = "https://login.windows.net/"; +process.env["ENDPOINT_DATA_AzureRMSpn_ACTIVEDIRECTORYSERVICEENDPOINTRESOURCEID"] = "https://login.windows.net/"; +process.env["ENDPOINT_DATA_AzureRMSpn_GRAPHURL"] = "https://graph.windows.net/"; +process.env["ENDPOINT_DATA_AzureRMSpn_AzureKeyVaultServiceEndpointResourceId"] = "https://vault.azure.net"; +process.env["ENDPOINT_URL_AzureRMSpn"] = "https://management.azure.com/"; +process.env["SYSTEM_DEFAULTWORKINGDIRECTORY"] = "C:\\a\\w\\"; +process.env["AGENT_TEMPDIRECTORY"] = process.cwd(); + +tr.registerMock('azure-pipelines-task-lib/toolrunner', require('azure-pipelines-task-lib/mock-toolrunner')); +tr.registerMock('./azure-arm-keyvault', require('./mock_node_modules/azure-arm-keyvault')); + +tr.run(); \ No newline at end of file diff --git a/Tasks/AzureKeyVaultV2/Tests/mock_node_modules/azure-arm-keyvault.ts b/Tasks/AzureKeyVaultV2/Tests/mock_node_modules/azure-arm-keyvault.ts new file mode 100644 index 000000000000..5423fa2adbd9 --- /dev/null +++ b/Tasks/AzureKeyVaultV2/Tests/mock_node_modules/azure-arm-keyvault.ts @@ -0,0 +1,82 @@ +'use strict'; + +class KeyVaultClient { + private credentials; + private subscriptionId; + private keyVaultName; + private keyVaultUrl; + + constructor(credentials, subscriptionId, keyVaultName, keyVaultUrl) { + this.credentials = credentials; + this.subscriptionId = subscriptionId; + this.keyVaultName = keyVaultName; + this.keyVaultUrl = keyVaultUrl; + } + + getSecrets = function (nextLink, callback) { + console.log("keyVaultClient.getSecrets is called"); + if (!callback) { + throw new Error('callback cannot be null.'); + } + // Validate + try { + if (this.keyVaultName === null || this.keyVaultName === undefined || typeof this.keyVaultName.valueOf() !== 'string') { + throw new Error('keyVaultName cannot be null or undefined and it must be of type string.'); + } + if (this.keyVaultUrl === null || this.keyVaultUrl === undefined || typeof this.keyVaultUrl.valueOf() !== 'string') { + throw new Error('keyVaultUrl cannot be null or undefined and it must be of type string.'); + } + } catch (error) { + callback(error); + } + + let secrets = [ + { + name: "secret1", + enabled: true, + expires: null, + contentType: "secret" + }, + { + name: "secret2", + enabled: true, + expires: null, + contentType: "secret" + }, + { + name: "secret3", + enabled: true, + expires: null, + contentType: "certificate" + }, + { + name: "secret4", + enabled: false, + expires: null, + contentType: "certificate" + }, + { + name: "secret5_%3B", + enabled: true, + expires: null, + contentType: "certificate" + } + ]; + callback(null, secrets); + }; + + getSecretValue = function (secretName, callback) { + console.log("getSecretValue is called for " + secretName); + + if (!callback) { + throw new Error('callback cannot be null.'); + } + if (secretName === null || secretName === undefined || typeof secretName.valueOf() !== 'string') { + throw new Error('secretName cannot be null or undefined and it must be of type string.'); + } + + callback(null, secretName + "-value"); + }; +} + +exports.KeyVaultClient = KeyVaultClient; \ No newline at end of file diff --git a/Tasks/AzureKeyVaultV2/ThirdPartyNotices.txt b/Tasks/AzureKeyVaultV2/ThirdPartyNotices.txt new file mode 100644 index 000000000000..0c1d270b46cb --- /dev/null +++ b/Tasks/AzureKeyVaultV2/ThirdPartyNotices.txt @@ -0,0 +1,1166 @@ + +THIRD-PARTY SOFTWARE NOTICES AND INFORMATION +Do Not Translate or Localize + +Microsoft Azure Key Vault incorporates third party material from the projects listed below. The original copyright notice and the license under which Microsoft received such third party material are set forth below. Microsoft reserves all other rights not expressly granted, whether by implication, estoppel or otherwise. + + +1. balanced-match (https://github.com/juliangruber/balanced-match) +2. brace-expansion (https://github.com/juliangruber/brace-expansion) +3. buffer-equal-constant-time (https://github.com/salesforce/buffer-equal-constant-time) +4. concat-map (https://github.com/substack/node-concat-map) +5. hoek (https://github.com/hapijs/hoek) + Includes:Deep-eql +6. isemail (https://github.com/hapijs/isemail) +7. joi (https://github.com/hapijs/joi) +8. jsonwebtoken (https://github.com/auth0/node-jsonwebtoken) +9. lodash.once (https://github.com/lodash/lodash) +10. minimatch (https://github.com/isaacs/minimatch) +11. Mockery (https://github.com/mfncooper/mockery) +12. Moment (https://github.com/moment/moment) + Includes:Files with code from Closure +13. ms (https://github.com/zeit/ms) +14. Node.js (https://nodejs.org/) +15. node-ecdsa-sig-formatter (https://github.com/Brightspace/node-ecdsa-sig-formatter) +16. node-jwa (https://github.com/brianloveswords/node-jwa) +17. node-jws (https://github.com/brianloveswords/node-jws) +18. node-uuid (https://github.com/broofa/node-uuid/) +19. OpenSSL (http://www.openssl.org) +20. Q (https://github.com/kriskowal/q) +21. safe-buffer (https://github.com/feross/safe-buffer) +22. sax js (https://github.com/isaacs/sax-js) +23. semver (https://github.com/npm/node-semver/) +24. ShellJS (https://github.com/shelljs/shelljs) +25. topo (https://github.com/hapijs/topo) +26. tunnel (https://github.com/koichik/node-tunnel) +27. underscore.js (http://underscorejs.org/; https://github.com/jashkenas/underscore) +28. uuid (https://github.com/kelektiv/node-uuid) +29. vso-node-api (https://github.com/Microsoft/vsts-node-api) +30. VSTS-task-lib (https://github.com/Microsoft/vsts-task-lib) +31. Xml2JS (https://github.com/Leonidas-from-XIV/node-xml2js) +32. Xmlbuilder (https://github.com/oozcitak/xmlbuilder-js) +33. xtend (https://github.com/Raynos/xtend) +34. @types/node (https://www.github.com/DefinitelyTyped/DefinitelyTyped.git) +35. @types/q (https://www.github.com/DefinitelyTyped/DefinitelyTyped.git) +36. @types/mocha (https://github.com/DefinitelyTyped/DefinitelyTyped.git) + + +%% balanced-match NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +(MIT) + +Copyright (c) 2013 Julian Gruber + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +========================================= +END OF balanced-match NOTICES, INFORMATION, AND LICENSE + +%% brace-expansion NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +(MIT) + +Copyright (c) 2013 Julian Gruber + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +========================================= +END OF brace-expansion NOTICES, INFORMATION, AND LICENSE + +%% buffer-equal-constant-time NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +Copyright (c) 2013, GoInstant Inc., a salesforce.com company +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +* Neither the name of salesforce.com, nor GoInstant, nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +========================================= +END OF buffer-equal-constant-time NOTICES, INFORMATION, AND LICENSE + +%% concat-map NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +Copyright (c) James Halliday/substack + +This software is released under the MIT license: + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +========================================= +END OF concat-map NOTICES, INFORMATION, AND LICENSE + +%% hoek NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= + +Copyright (c) 2011-2014, Walmart and other contributors. +Copyright (c) 2011, Yahoo Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * The names of any contributors may not be used to endorse or promote + products derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + * * * + +The complete list of contributors can be found at: https://github.com/hapijs/hapi/graphs/contributors +Portions of this project were initially based on the Yahoo! Inc. Postmile project, +published at https://github.com/yahoo/postmile. +========================================= +Includes code from Deep-eql + +Copyright (c) 2013 Jake Luer jake@alogicalparadox.com + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +========================================= +END OF hoek NOTICES, INFORMATION, AND LICENSE + +%% isemail NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +Copyright (c) 2008-2011, Dominic Sayers +Copyright (c) 2013-2014, GlobeSherpa +Copyright (c) 2014-2015, Eli Skeggs + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +- Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +- Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +- Neither the name of Dominic Sayers nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +========================================= +END OF isemail NOTICES, INFORMATION, AND LICENSE + +%% joi NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +Copyright (c) 2012-2014, Walmart and other contributors. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * The names of any contributors may not be used to endorse or promote + products derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + * * * + +The complete list of contributors can be found at: https://github.com/hapijs/joi/graphs/contributors +========================================= +END OF joi NOTICES, INFORMATION, AND LICENSE + +%% jsonwebtoken NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +The MIT License (MIT) + +Copyright (c) 2015 Auth0, Inc. (http://auth0.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +========================================= +END OF jsonwebtoken NOTICES, INFORMATION, AND LICENSE + +%% lodash.once NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +Copyright jQuery Foundation and other contributors + +Based on Underscore.js, copyright Jeremy Ashkenas, +DocumentCloud and Investigative Reporters & Editors + +This software consists of voluntary contributions made by many +individuals. For exact contribution history, see the revision history +available at https://github.com/lodash/lodash + +The following license applies to all parts of this software except as +documented below: + +==== + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +==== + +Copyright and related rights for sample code are waived via CC0. Sample +code is defined as all source code displayed within the prose of the +documentation. + +CC0: http://creativecommons.org/publicdomain/zero/1.0/ + +==== + +Files located in the node_modules and vendor directories are externally +maintained libraries used by this software which have their own +licenses; we recommend you read them, as their terms may differ from the +terms above. +========================================= +END OF lodash.once NOTICES, INFORMATION, AND LICENSE + +%% minimatch NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +The ISC License + +Copyright (c) Isaac Z. Schlueter and Contributors + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +========================================= +END OF minimatch NOTICES, INFORMATION, AND LICENSE + +%% Mockery NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +Copyrights for code authored by Yahoo! Inc. is licensed under the following +terms: + + MIT License + + Copyright (c) 2011 Yahoo! Inc. All Rights Reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to + deal in the Software without restriction, including without limitation the + rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + sell copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +========================================= +END OF Mockery NOTICES, INFORMATION, AND LICENSE + +%% Moment NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +Copyright (c) JS Foundation and other contributors + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. +========================================= +Files with code from Closure + +Copyright (c) 2006 The Closure Library Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +========================================= +END OF Moment NOTICES, INFORMATION, AND LICENSE + +%% ms NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +(The MIT License) + +Copyright (c) 2014 Guillermo Rauch +Copyright (c) 2016 Zeit, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the Software), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +========================================= +END OF ms NOTICES, INFORMATION, AND LICENSE + +%% Node.js NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +Node.js is licensed for use as follows: + +""" +Copyright Node.js contributors. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. +""" + +This license applies to parts of Node.js originating from the +https://github.com/joyent/node repository: + +""" +Copyright Joyent, Inc. and other Node contributors. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. +""" + +The Node.js license applies to all parts of Node.js that are not externally +maintained libraries. +========================================= +END OF Node.js NOTICES, INFORMATION, AND LICENSE + +%% node-ecdsa-sig-formatter NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= + Copyright 2015 D2L Corporation + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +========================================= +END OF node-ecdsa-sig-formatter NOTICES, INFORMATION, AND LICENSE + +%% node-jwa NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +Copyright (c) 2013 Brian J. Brennan + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the +Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +========================================= +END OF node-jwa NOTICES, INFORMATION, AND LICENSE + +%% node-jws NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +Copyright (c) 2013 Brian J. Brennan + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the +Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +========================================= +END OF node-jws NOTICES, INFORMATION, AND LICENSE + +%% node-uuid NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +The MIT License (MIT) + +Copyright (c) 2010-2012 Robert Kieffer + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the Software), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +========================================= +END OF node-uuid NOTICES, INFORMATION, AND LICENSE + +%% OpenSSL NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= + LICENSE ISSUES + ============== + + The OpenSSL toolkit stays under a dual license, i.e. both the conditions of + the OpenSSL License and the original SSLeay license apply to the toolkit. + See below for the actual license texts. Actually both licenses are BSD-style + Open Source licenses. In case of any license issues related to OpenSSL + please contact openssl-core@openssl.org. + + OpenSSL License + --------------- + +/* ==================================================================== + * Copyright (c) 1998-2011 The OpenSSL Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.openssl.org/) + * + * 4. The names OpenSSL Toolkit and OpenSSL Project must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * openssl-core@openssl.org. + * + * 5. Products derived from this software may not be called OpenSSL + * nor may OpenSSL appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.openssl.org/) + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This product includes cryptographic software written by Eric Young + * (eay@cryptsoft.com). This product includes software written by Tim + * Hudson (tjh@cryptsoft.com). + * + */ + + Original SSLeay License + ----------------------- + +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com) + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * This product includes software written by Tim Hudson (tjh@cryptsoft.com) + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] +========================================= +END OF OpenSSL NOTICES, INFORMATION, AND LICENSE + +%% Q NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +Copyright 2009�2014 Kristopher Michael Kowal. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. + +The file q.js is prefaced by the following additional third-party subcomponent information: + +/*! + * + * Copyright 2009-2012 Kris Kowal under the terms of the MIT + * license found at http://github.com/kriskowal/q/raw/master/LICENSE + * + * With parts by Tyler Close + * Copyright 2007-2009 Tyler Close under the terms of the MIT X license found + * at http://www.opensource.org/licenses/mit-license.html + * Forked at ref_send.js version: 2009-05-11 + * + * With parts by Mark Miller + * Copyright (C) 2011 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +========================================= +END OF Q NOTICES, INFORMATION, AND LICENSE + +%% safe-buffer NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +The MIT License (MIT) + +Copyright (c) Feross Aboukhadijeh + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +========================================= +END OF safe-buffer NOTICES, INFORMATION, AND LICENSE + +%% sax js NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +The ISC License + +Copyright (c) Isaac Z. Schlueter and Contributors + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +==== + +`String.fromCodePoint` by Mathias Bynens used according to terms of MIT +License, as follows: + + Copyright Mathias Bynens + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +========================================= +END OF sax js NOTICES, INFORMATION, AND LICENSE + +%% semver NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +The ISC License + +Copyright (c) Isaac Z. Schlueter and Contributors + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +========================================= +END OF semver NOTICES, INFORMATION, AND LICENSE + +%% ShellJS NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +Copyright (c) 2012, Artur Adib +All rights reserved. + +You may use this project under the terms of the New BSD license as follows: + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Artur Adib nor the + names of the contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL ARTUR ADIB BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +========================================= +END OF ShellJS NOTICES, INFORMATION, AND LICENSE + +%% topo NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +Copyright (c) 2012-2014, Walmart and other contributors. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * The names of any contributors may not be used to endorse or promote + products derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + * * * + +The complete list of contributors can be found at: https://github.com/hapijs/topo/graphs/contributors +========================================= +END OF topo NOTICES, INFORMATION, AND LICENSE + +%% tunnel NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +The MIT License (MIT) + +Copyright (c) 2012 Koichi Kobayashi + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +========================================= +END OF tunnel NOTICES, INFORMATION, AND LICENSE + +%% underscore.js NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +Copyright (c) 2009-2017 Jeremy Ashkenas, DocumentCloud and Investigative +Reporters & Editors + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +========================================= +END OF underscore.js NOTICES, INFORMATION, AND LICENSE + +%% uuid NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +The MIT License (MIT) + +Copyright (c) 2010-2016 Robert Kieffer and other contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +========================================= +END OF uuid NOTICES, INFORMATION, AND LICENSE + +%% vso-node-api NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +Copyright (c) Microsoft Corporation + +All rights reserved. + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT +LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +========================================= +END OF vso-node-api NOTICES, INFORMATION, AND LICENSE + +%% VSTS-task-lib NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +The MIT License (MIT) + +Copyright (c) Microsoft Corporation. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +========================================= +END OF VSTS-task-lib NOTICES, INFORMATION, AND LICENSE + +%% Xml2JS NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +Copyright 2010, 2011, 2012, 2013. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. +========================================= +END OF Xml2JS NOTICES, INFORMATION, AND LICENSE + +%% Xmlbuilder NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +The MIT License (MIT) + +Copyright (c) 2013 Ozgur Ozcitak + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +========================================= +END OF Xmlbuilder NOTICES, INFORMATION, AND LICENSE + +%% xtend NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +Copyright (c) 2012-2014 Raynos. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the Software), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +========================================= +END OF xtend NOTICES, INFORMATION, AND LICENSE + +%% @types/mocha NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +The MIT License (MIT) + +Copyright (c) Microsoft Corporation. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +========================================= +END OF @types/mocha NOTICES, INFORMATION, AND LICENSE + +%% @types/node NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +MIT License + +Copyright (c) Microsoft Corporation. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +========================================= +END OF @types/node NOTICES, INFORMATION, AND LICENSE + +%% @types/q NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +MIT License + +Copyright (c) Microsoft Corporation. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +========================================= +END OF @types/q NOTICES, INFORMATION, AND LICENSE \ No newline at end of file diff --git a/Tasks/AzureKeyVaultV2/icon.png b/Tasks/AzureKeyVaultV2/icon.png new file mode 100644 index 000000000000..ddffb100f7bf Binary files /dev/null and b/Tasks/AzureKeyVaultV2/icon.png differ diff --git a/Tasks/AzureKeyVaultV2/icon.svg b/Tasks/AzureKeyVaultV2/icon.svg new file mode 100644 index 000000000000..04103ba9bfa7 --- /dev/null +++ b/Tasks/AzureKeyVaultV2/icon.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/Tasks/AzureKeyVaultV2/make.json b/Tasks/AzureKeyVaultV2/make.json new file mode 100644 index 000000000000..9ca2598c1ea4 --- /dev/null +++ b/Tasks/AzureKeyVaultV2/make.json @@ -0,0 +1,11 @@ +{ + "rm": [ + { + "items": [ + "node_modules/azure-pipelines-tasks-azure-arm-rest-v2/node_modules/azure-pipelines-task-lib", + "node_modules/azure-pipelines-tasks-azure-arm-rest-v2/node_modules/vso-node-api" + ], + "options": "-Rf" + } + ] +} \ No newline at end of file diff --git a/Tasks/AzureKeyVaultV2/models/KeyVaultTaskParameters.ts b/Tasks/AzureKeyVaultV2/models/KeyVaultTaskParameters.ts new file mode 100644 index 000000000000..ecccb7dd2f42 --- /dev/null +++ b/Tasks/AzureKeyVaultV2/models/KeyVaultTaskParameters.ts @@ -0,0 +1,53 @@ +import msRestAzure = require('azure-pipelines-tasks-azure-arm-rest-v2/azure-arm-common'); +import tl = require("azure-pipelines-task-lib/task"); +import util = require("util"); + +import { AzureEndpoint } from 'azure-pipelines-tasks-azure-arm-rest-v2/azureModels'; +import { AzureRMEndpoint } from 'azure-pipelines-tasks-azure-arm-rest-v2/azure-arm-endpoint'; + +export class KeyVaultTaskParameters { + + public subscriptionId: string; + public keyVaultName: string; + public secretsFilter: string[]; + public vaultCredentials: msRestAzure.ApplicationTokenCredentials; + public keyVaultUrl: string; + public servicePrincipalId: string; + public scheme: string; + + private _environments = { + 'AzureStack': 'azurestack' + } + + public async getKeyVaultTaskParameters() : Promise { + var connectedService = tl.getInput("ConnectedServiceName", true); + this.subscriptionId = tl.getEndpointDataParameter(connectedService, "SubscriptionId", true); + this.keyVaultName = tl.getInput("KeyVaultName", true); + this.secretsFilter = tl.getDelimitedInput("SecretsFilter", ",", true); + var azureKeyVaultDnsSuffix = tl.getEndpointDataParameter(connectedService, "AzureKeyVaultDnsSuffix", true); + if (!azureKeyVaultDnsSuffix) { + azureKeyVaultDnsSuffix = "vault.azure.net" + } + this.servicePrincipalId = tl.getEndpointAuthorizationParameter(connectedService, 'serviceprincipalid', true); + this.keyVaultUrl = util.format("https://%s.%s", this.keyVaultName, azureKeyVaultDnsSuffix); + this.scheme = tl.getEndpointAuthorizationScheme(connectedService, false); + this.vaultCredentials = await this.getVaultCredentials(connectedService); + return this; + } + + private async getVaultCredentials(connectedService: string): Promise { + const endpoint: AzureEndpoint = await new AzureRMEndpoint(connectedService).getEndpoint(); + + if(!!endpoint.environment && endpoint.environment.toLowerCase() == this._environments.AzureStack) { + endpoint.applicationTokenCredentials.activeDirectoryResourceId = endpoint.activeDirectoryResourceID.replace("management", "vault"); + } else { + if (!endpoint.azureKeyVaultServiceEndpointResourceId) { + endpoint.azureKeyVaultServiceEndpointResourceId = "https://vault.azure.net"; + } + endpoint.applicationTokenCredentials.baseUrl = endpoint.azureKeyVaultServiceEndpointResourceId; + endpoint.applicationTokenCredentials.activeDirectoryResourceId = endpoint.azureKeyVaultServiceEndpointResourceId; + } + + return endpoint.applicationTokenCredentials; + } +} \ No newline at end of file diff --git a/Tasks/AzureKeyVaultV2/npm-shrinkwrap.json b/Tasks/AzureKeyVaultV2/npm-shrinkwrap.json new file mode 100644 index 000000000000..45e6112a79d7 --- /dev/null +++ b/Tasks/AzureKeyVaultV2/npm-shrinkwrap.json @@ -0,0 +1,617 @@ +{ + "name": "AzureKeyVault", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "AzureKeyVault", + "dependencies": { + "@types/mocha": "^5.2.7", + "@types/node": "^10.17.0", + "@types/q": "^1.5.0", + "@types/uuid": "^8.3.0", + "azure-pipelines-task-lib": "2.8.0", + "azure-pipelines-tasks-azure-arm-rest-v2": "1.0.4", + "moment": "2.21.0", + "vso-node-api": "6.0.1-preview" + }, + "devDependencies": { + "typescript": "4.0.2" + } + }, + "node_modules/@types/mocha": { + "version": "5.2.7", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-5.2.7.tgz", + "integrity": "sha512-NYrtPht0wGzhwe9+/idPaBB+TqkY9AhTvOLMkThm0IoEfLaiVQZwBwyJ5puCkO3AUCWrmcoePjp2mbFocKy4SQ==" + }, + "node_modules/@types/node": { + "version": "10.17.48", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.48.tgz", + "integrity": "sha512-Agl6xbYP6FOMDeAsr3QVZ+g7Yzg0uhPHWx0j5g4LFdUBHVtqtU+gH660k/lCEe506jJLOGbEzsnqPDTZGJQLag==" + }, + "node_modules/@types/q": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.4.tgz", + "integrity": "sha512-1HcDas8SEj4z1Wc696tH56G8OlRaH/sqZOynNNB+HF0WOeXPaxTtbYzJY2oEfiUxjSKjhCKr+MvR7dCHcEelug==" + }, + "node_modules/@types/uuid": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.0.tgz", + "integrity": "sha512-eQ9qFW/fhfGJF8WKHGEHZEyVWfZxrT+6CLIJGBcZPfxUh/+BnEj+UCGYMlr9qZuX/2AltsvwrGqp0LhEW8D0zQ==" + }, + "node_modules/azure-pipelines-task-lib": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/azure-pipelines-task-lib/-/azure-pipelines-task-lib-2.8.0.tgz", + "integrity": "sha512-PR8oap9z2j+o455W3PwAfB4SX1p4GdJc9OHQaQV0V+iQS1IBY6dVgcNSQMkHAXb0V1bbuLOFBLanXPe5eSgGTQ==", + "dependencies": { + "minimatch": "3.0.4", + "mockery": "^1.7.0", + "q": "^1.1.2", + "semver": "^5.1.0", + "shelljs": "^0.3.0", + "uuid": "^3.0.1" + } + }, + "node_modules/azure-pipelines-tasks-azure-arm-rest-v2": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/azure-pipelines-tasks-azure-arm-rest-v2/-/azure-pipelines-tasks-azure-arm-rest-v2-1.0.4.tgz", + "integrity": "sha512-aEo1zwRZ5sYSh6AzZEWP+nFrD9TcnqoNlNy+7ItLEPKr266ToAM97vEj8vFqgtoAapMHawlErN173Uwq6TnRIA==", + "dependencies": { + "@types/mocha": "2.2.48", + "@types/node": "6.0.68", + "@types/q": "1.0.7", + "azure-pipelines-task-lib": "2.8.0", + "jsonwebtoken": "7.3.0", + "q": "1.4.1", + "typed-rest-client": "1.7.3" + } + }, + "node_modules/azure-pipelines-tasks-azure-arm-rest-v2/node_modules/@types/mocha": { + "version": "2.2.48", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-2.2.48.tgz", + "integrity": "sha512-nlK/iyETgafGli8Zh9zJVCTicvU3iajSkRwOh3Hhiva598CMqNJ4NcVCGMTGKpGpTYj/9R8RLzS9NAykSSCqGw==" + }, + "node_modules/azure-pipelines-tasks-azure-arm-rest-v2/node_modules/@types/node": { + "version": "6.0.68", + "resolved": "https://registry.npmjs.org/@types/node/-/node-6.0.68.tgz", + "integrity": "sha1-DEO2uLlEX+uGoPvTRX4/S8WR5m0=" + }, + "node_modules/azure-pipelines-tasks-azure-arm-rest-v2/node_modules/@types/q": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@types/q/-/q-1.0.7.tgz", + "integrity": "sha512-0WS7XU7sXzQ7J1nbnMKKYdjrrFoO3YtZYgUzeV8JFXffPnHfvSJQleR70I8BOAsOm14i4dyaAZ3YzqIl1YhkXQ==" + }, + "node_modules/azure-pipelines-tasks-azure-arm-rest-v2/node_modules/q": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.4.1.tgz", + "integrity": "sha1-VXBbzZPF82c1MMLCy8DCs63cKG4=", + "engines": { + "node": ">=0.6.0", + "teleport": ">=0.2.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/hoek": { + "version": "2.16.3", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", + "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=", + "engines": { + "node": ">=0.10.40" + } + }, + "node_modules/isemail": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/isemail/-/isemail-1.2.0.tgz", + "integrity": "sha1-vgPfjMPineTSxd9lASY/H6RZXpo=", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/joi": { + "version": "6.10.1", + "resolved": "https://registry.npmjs.org/joi/-/joi-6.10.1.tgz", + "integrity": "sha1-TVDDGAeRIgAP5fFq8f+OGRe3fgY=", + "dependencies": { + "hoek": "2.x.x", + "isemail": "1.x.x", + "moment": "2.x.x", + "topo": "1.x.x" + }, + "engines": { + "node": ">=0.10.40", + "npm": ">=2.0.0" + } + }, + "node_modules/jsonwebtoken": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-7.3.0.tgz", + "integrity": "sha1-hRGNanDj/M3xQ4n056HD+cip+7o=", + "dependencies": { + "joi": "^6.10.1", + "jws": "^3.1.4", + "lodash.once": "^4.0.0", + "ms": "^0.7.1", + "xtend": "^4.0.1" + }, + "engines": { + "node": ">=0.12", + "npm": ">=1.4.28" + } + }, + "node_modules/jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=" + }, + "node_modules/minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/mockery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/mockery/-/mockery-1.7.0.tgz", + "integrity": "sha1-9O3g2HUMHJcnwnLqLGBiniyaHE8=" + }, + "node_modules/moment": { + "version": "2.21.0", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.21.0.tgz", + "integrity": "sha512-TCZ36BjURTeFTM/CwRcViQlfkMvL1/vFISuNLO5GkcVm1+QHfbSiNqZuWeMFjj1/3+uAjXswgRk30j1kkLYJBQ==", + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.3.tgz", + "integrity": "sha1-cIFVpeROM/X9D8U+gdDUCpG+H/8=" + }, + "node_modules/q": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", + "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=", + "engines": { + "node": ">=0.6.0", + "teleport": ">=0.2.0" + } + }, + "node_modules/qs": { + "version": "6.9.6", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.6.tgz", + "integrity": "sha512-TIRk4aqYLNoJUbd+g2lEdz5kLWIuTMRagAXxl78Q0RiVjAOugHmeKNGdd3cwo/ktpf9aL9epCfFqWDEKysUlLQ==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + }, + "node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/shelljs": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.3.0.tgz", + "integrity": "sha1-NZbmMHp4FUT1kfN9phg2DzHbV7E=", + "bin": { + "shjs": "bin/shjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/topo": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/topo/-/topo-1.1.0.tgz", + "integrity": "sha1-6ddRYV0buH3IZdsYL6HKCl71NtU=", + "dependencies": { + "hoek": "2.x.x" + }, + "engines": { + "node": ">=0.10.40" + } + }, + "node_modules/tunnel": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.4.tgz", + "integrity": "sha1-LTeFoVjBdMmhbcLARuxfxfF0IhM=", + "engines": { + "node": ">=0.6.11 <=0.7.0 || >=0.7.3" + } + }, + "node_modules/typed-rest-client": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/typed-rest-client/-/typed-rest-client-1.7.3.tgz", + "integrity": "sha512-CwTpx/TkRHGZoHkJhBcp4X8K3/WtlzSHVQR0OIFnt10j4tgy4ypgq/SrrgVpA1s6tAL49Q6J3R5C0Cgfh2ddqA==", + "dependencies": { + "qs": "^6.9.1", + "tunnel": "0.0.6", + "underscore": "1.8.3" + } + }, + "node_modules/typed-rest-client/node_modules/tunnel": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", + "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", + "engines": { + "node": ">=0.6.11 <=0.7.0 || >=0.7.3" + } + }, + "node_modules/typescript": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.0.2.tgz", + "integrity": "sha512-e4ERvRV2wb+rRZ/IQeb3jm2VxBsirQLpQhdxplZ2MEzGvDkkMmPglecnNDfSUBivMjP93vRbngYYDQqQ/78bcQ==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/underscore": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz", + "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=" + }, + "node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "bin": { + "uuid": "bin/uuid" + } + }, + "node_modules/vso-node-api": { + "version": "6.0.1-preview", + "resolved": "https://registry.npmjs.org/vso-node-api/-/vso-node-api-6.0.1-preview.tgz", + "integrity": "sha1-RBprv5s8aNpiTbAeo1y6jwpMLKs=", + "dependencies": { + "q": "^1.0.1", + "tunnel": "0.0.4", + "underscore": "^1.8.3" + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "engines": { + "node": ">=0.4" + } + } + }, + "dependencies": { + "@types/mocha": { + "version": "5.2.7", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-5.2.7.tgz", + "integrity": "sha512-NYrtPht0wGzhwe9+/idPaBB+TqkY9AhTvOLMkThm0IoEfLaiVQZwBwyJ5puCkO3AUCWrmcoePjp2mbFocKy4SQ==" + }, + "@types/node": { + "version": "10.17.48", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.48.tgz", + "integrity": "sha512-Agl6xbYP6FOMDeAsr3QVZ+g7Yzg0uhPHWx0j5g4LFdUBHVtqtU+gH660k/lCEe506jJLOGbEzsnqPDTZGJQLag==" + }, + "@types/q": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.4.tgz", + "integrity": "sha512-1HcDas8SEj4z1Wc696tH56G8OlRaH/sqZOynNNB+HF0WOeXPaxTtbYzJY2oEfiUxjSKjhCKr+MvR7dCHcEelug==" + }, + "@types/uuid": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.0.tgz", + "integrity": "sha512-eQ9qFW/fhfGJF8WKHGEHZEyVWfZxrT+6CLIJGBcZPfxUh/+BnEj+UCGYMlr9qZuX/2AltsvwrGqp0LhEW8D0zQ==" + }, + "azure-pipelines-task-lib": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/azure-pipelines-task-lib/-/azure-pipelines-task-lib-2.8.0.tgz", + "integrity": "sha512-PR8oap9z2j+o455W3PwAfB4SX1p4GdJc9OHQaQV0V+iQS1IBY6dVgcNSQMkHAXb0V1bbuLOFBLanXPe5eSgGTQ==", + "requires": { + "minimatch": "3.0.4", + "mockery": "^1.7.0", + "q": "^1.1.2", + "semver": "^5.1.0", + "shelljs": "^0.3.0", + "uuid": "^3.0.1" + } + }, + "azure-pipelines-tasks-azure-arm-rest-v2": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/azure-pipelines-tasks-azure-arm-rest-v2/-/azure-pipelines-tasks-azure-arm-rest-v2-1.0.4.tgz", + "integrity": "sha512-aEo1zwRZ5sYSh6AzZEWP+nFrD9TcnqoNlNy+7ItLEPKr266ToAM97vEj8vFqgtoAapMHawlErN173Uwq6TnRIA==", + "requires": { + "@types/mocha": "2.2.48", + "@types/node": "6.0.68", + "@types/q": "1.0.7", + "azure-pipelines-task-lib": "2.8.0", + "jsonwebtoken": "7.3.0", + "q": "1.4.1", + "typed-rest-client": "1.7.3" + }, + "dependencies": { + "@types/mocha": { + "version": "2.2.48", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-2.2.48.tgz", + "integrity": "sha512-nlK/iyETgafGli8Zh9zJVCTicvU3iajSkRwOh3Hhiva598CMqNJ4NcVCGMTGKpGpTYj/9R8RLzS9NAykSSCqGw==" + }, + "@types/node": { + "version": "6.0.68", + "resolved": "https://registry.npmjs.org/@types/node/-/node-6.0.68.tgz", + "integrity": "sha1-DEO2uLlEX+uGoPvTRX4/S8WR5m0=" + }, + "@types/q": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@types/q/-/q-1.0.7.tgz", + "integrity": "sha512-0WS7XU7sXzQ7J1nbnMKKYdjrrFoO3YtZYgUzeV8JFXffPnHfvSJQleR70I8BOAsOm14i4dyaAZ3YzqIl1YhkXQ==" + }, + "q": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.4.1.tgz", + "integrity": "sha1-VXBbzZPF82c1MMLCy8DCs63cKG4=" + } + } + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "hoek": { + "version": "2.16.3", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", + "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=" + }, + "isemail": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/isemail/-/isemail-1.2.0.tgz", + "integrity": "sha1-vgPfjMPineTSxd9lASY/H6RZXpo=" + }, + "joi": { + "version": "6.10.1", + "resolved": "https://registry.npmjs.org/joi/-/joi-6.10.1.tgz", + "integrity": "sha1-TVDDGAeRIgAP5fFq8f+OGRe3fgY=", + "requires": { + "hoek": "2.x.x", + "isemail": "1.x.x", + "moment": "2.x.x", + "topo": "1.x.x" + } + }, + "jsonwebtoken": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-7.3.0.tgz", + "integrity": "sha1-hRGNanDj/M3xQ4n056HD+cip+7o=", + "requires": { + "joi": "^6.10.1", + "jws": "^3.1.4", + "lodash.once": "^4.0.0", + "ms": "^0.7.1", + "xtend": "^4.0.1" + } + }, + "jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "requires": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "requires": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, + "lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=" + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "mockery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/mockery/-/mockery-1.7.0.tgz", + "integrity": "sha1-9O3g2HUMHJcnwnLqLGBiniyaHE8=" + }, + "moment": { + "version": "2.21.0", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.21.0.tgz", + "integrity": "sha512-TCZ36BjURTeFTM/CwRcViQlfkMvL1/vFISuNLO5GkcVm1+QHfbSiNqZuWeMFjj1/3+uAjXswgRk30j1kkLYJBQ==" + }, + "ms": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.3.tgz", + "integrity": "sha1-cIFVpeROM/X9D8U+gdDUCpG+H/8=" + }, + "q": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", + "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=" + }, + "qs": { + "version": "6.9.6", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.6.tgz", + "integrity": "sha512-TIRk4aqYLNoJUbd+g2lEdz5kLWIuTMRagAXxl78Q0RiVjAOugHmeKNGdd3cwo/ktpf9aL9epCfFqWDEKysUlLQ==" + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + }, + "shelljs": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.3.0.tgz", + "integrity": "sha1-NZbmMHp4FUT1kfN9phg2DzHbV7E=" + }, + "topo": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/topo/-/topo-1.1.0.tgz", + "integrity": "sha1-6ddRYV0buH3IZdsYL6HKCl71NtU=", + "requires": { + "hoek": "2.x.x" + } + }, + "tunnel": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.4.tgz", + "integrity": "sha1-LTeFoVjBdMmhbcLARuxfxfF0IhM=" + }, + "typed-rest-client": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/typed-rest-client/-/typed-rest-client-1.7.3.tgz", + "integrity": "sha512-CwTpx/TkRHGZoHkJhBcp4X8K3/WtlzSHVQR0OIFnt10j4tgy4ypgq/SrrgVpA1s6tAL49Q6J3R5C0Cgfh2ddqA==", + "requires": { + "qs": "^6.9.1", + "tunnel": "0.0.6", + "underscore": "1.8.3" + }, + "dependencies": { + "tunnel": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", + "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==" + } + } + }, + "typescript": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.0.2.tgz", + "integrity": "sha512-e4ERvRV2wb+rRZ/IQeb3jm2VxBsirQLpQhdxplZ2MEzGvDkkMmPglecnNDfSUBivMjP93vRbngYYDQqQ/78bcQ==", + "dev": true + }, + "underscore": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz", + "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=" + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" + }, + "vso-node-api": { + "version": "6.0.1-preview", + "resolved": "https://registry.npmjs.org/vso-node-api/-/vso-node-api-6.0.1-preview.tgz", + "integrity": "sha1-RBprv5s8aNpiTbAeo1y6jwpMLKs=", + "requires": { + "q": "^1.0.1", + "tunnel": "0.0.4", + "underscore": "^1.8.3" + } + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" + } + } +} diff --git a/Tasks/AzureKeyVaultV2/operations/KeyVault.ts b/Tasks/AzureKeyVaultV2/operations/KeyVault.ts new file mode 100644 index 000000000000..142d598b083e --- /dev/null +++ b/Tasks/AzureKeyVaultV2/operations/KeyVault.ts @@ -0,0 +1,274 @@ +import keyVaultTaskParameters = require("../models/KeyVaultTaskParameters"); +import armKeyVault = require("./azure-arm-keyvault"); +import util = require("util"); +import tl = require("azure-pipelines-task-lib/task"); + +import * as path from 'path'; +import * as fs from 'fs'; + +export class SecretsToErrorsMapping { + public errorsMap: { [key: string]: string; }; + + constructor() { + this.errorsMap = {}; + } + + public addError(secretName: string, errorMessage: string): void { + this.errorsMap[secretName] = errorMessage; + } + + public isEmpty(): boolean { + for (var key in this.errorsMap) { + return false; + } + + return true; + } + + public getAllErrors(): string { + var allErrors = ""; + for (var key in this.errorsMap) { + if (this.errorsMap.hasOwnProperty(key)) { + var errorMessagePerSecret = key + ": " + JSON.stringify(this.errorsMap[key]); + allErrors = allErrors + "\n" + errorMessagePerSecret; + } + } + + return allErrors; + } +} + +export class KeyVault { + + private taskParameters: keyVaultTaskParameters.KeyVaultTaskParameters; + private keyVaultClient: armKeyVault.KeyVaultClient; + private provisionKeyVaultSecretsScript: string; + + constructor(taskParameters: keyVaultTaskParameters.KeyVaultTaskParameters) { + this.taskParameters = taskParameters; + + this.keyVaultClient = new armKeyVault.KeyVaultClient( + this.taskParameters.vaultCredentials, + this.taskParameters.subscriptionId, + this.taskParameters.keyVaultName, + this.taskParameters.keyVaultUrl); + + let scriptContentFormat; + if(this.taskParameters.scheme === "ManagedServiceIdentity") { + scriptContentFormat = `$ErrorActionPreference=\"Stop\"; + Login-AzureRmAccount -SubscriptionId %s; + $vmMetadata = Invoke-RestMethod -Headers @{"Metadata"="true"} -URI http://169.254.169.254/metadata/instance?api-version=2017-08-01 -Method get + $vm = Get-AzureRmVM -ResourceGroupName $vmMetadata.compute.resourceGroupName -Name $vmMetadata.compute.name + $spn=(Get-AzureRmADServicePrincipal -SPN %s); + Set-AzureRmKeyVaultAccessPolicy -VaultName %s -ObjectId $vm.Identity.PrincipalId -PermissionsToSecrets get,list;`; + } else { + scriptContentFormat = `$ErrorActionPreference=\"Stop\"; + Login-AzureRmAccount -SubscriptionId %s; + $spn=(Get-AzureRmADServicePrincipal -SPN %s); + $spnObjectId=$spn.Id; + Set-AzureRmKeyVaultAccessPolicy -VaultName %s -ObjectId $spnObjectId -PermissionsToSecrets get,list;`; + } + + this.provisionKeyVaultSecretsScript = util.format(scriptContentFormat, this.taskParameters.subscriptionId, this.taskParameters.servicePrincipalId, this.taskParameters.keyVaultName); + } + + public downloadSecrets(secretsToErrorsMap: SecretsToErrorsMapping): Promise { + + var downloadAllSecrets = false; + if (this.taskParameters.secretsFilter && this.taskParameters.secretsFilter.length > 0) + { + if (this.taskParameters.secretsFilter.length === 1 && this.taskParameters.secretsFilter[0] === "*") { + downloadAllSecrets = true; + } + } else { + downloadAllSecrets = true; + } + + console.log(tl.loc("SubscriptionIdLabel", this.taskParameters.subscriptionId)); + console.log(tl.loc("KeyVaultNameLabel", this.taskParameters.keyVaultName)); + + // Key vault task explicitly handles multi line masking - hence setting SYSTEM_UNSAFEALLOWMULTILINESECRET to true + tl.setVariable("SYSTEM_UNSAFEALLOWMULTILINESECRET", "true"); + + if (downloadAllSecrets) { + return this.downloadAllSecrets(secretsToErrorsMap); + } else { + return this.downloadSelectedSecrets(this.taskParameters.secretsFilter, secretsToErrorsMap); + } + } + + private downloadAllSecrets(secretsToErrorsMap: SecretsToErrorsMapping): Promise { + tl.debug(util.format("Downloading all secrets from subscriptionId: %s, vault: %s", this.taskParameters.subscriptionId, this.taskParameters.keyVaultName)); + + return new Promise((resolve, reject) => { + this.keyVaultClient.getSecrets("", (error, listOfSecrets, request, response) => { + if (error) { + return reject(tl.loc("GetSecretsFailed", this.getError(error))); + } + + if (listOfSecrets.length == 0) { + console.log(tl.loc("NoSecretsFound", this.taskParameters.keyVaultName)); + return resolve(); + } + + console.log(tl.loc("NumberOfSecretsFound", this.taskParameters.keyVaultName, listOfSecrets.length)); + listOfSecrets = this.filterDisabledAndExpiredSecrets(listOfSecrets); + console.log(tl.loc("NumberOfEnabledSecretsFound", this.taskParameters.keyVaultName, listOfSecrets.length)); + + var getSecretValuePromises: Promise[] = []; + listOfSecrets.forEach((secret: armKeyVault.AzureKeyVaultSecret, index: number) => { + getSecretValuePromises.push(this.downloadSecretValue(secret.name, secretsToErrorsMap)); + }); + + Promise.all(getSecretValuePromises).then(() =>{ + return resolve(); + }); + }); + }); + } + + private downloadSelectedSecrets(selectedSecrets: string[], secretsToErrorsMap: SecretsToErrorsMapping): Promise { + tl.debug(util.format("Downloading selected secrets from subscriptionId: %s, vault: %s", this.taskParameters.subscriptionId, this.taskParameters.keyVaultName)); + + return new Promise((resolve, reject) => { + var getSecretValuePromises: Promise[] = []; + selectedSecrets.forEach((secretName: string, index: number) => { + getSecretValuePromises.push(this.downloadSecretValue(secretName, secretsToErrorsMap)); + }); + + Promise.all(getSecretValuePromises).then(() =>{ + return resolve(); + }); + }); + } + + private filterDisabledAndExpiredSecrets(listOfSecrets: armKeyVault.AzureKeyVaultSecret[]): armKeyVault.AzureKeyVaultSecret[] { + var result: armKeyVault.AzureKeyVaultSecret[] = []; + var now: Date = new Date(); + + listOfSecrets.forEach((value: armKeyVault.AzureKeyVaultSecret, index: number) => { + if (value.enabled && (!value.expires || value.expires > now)) { + result.push(value); + } + }); + + return result; + } + + private downloadSecretValue(secretName: string, secretsToErrorsMap: SecretsToErrorsMapping): Promise { + tl.debug(util.format("Promise for downloading secret value for: %s", secretName)); + secretName = secretName.trim(); + + return new Promise((resolve, reject) => { + this.keyVaultClient.getSecretValue(secretName, (error, secretValue, request, response) => { + if (error) { + let errorMessage = this.getError(error); + secretsToErrorsMap.addError(secretName, errorMessage); + } + else { + this.setVaultVariable(secretName, secretValue); + } + + return resolve(); + }); + }); + } + + private tryFlattenJson(jsonString: string): string { + try { + var o = JSON.parse(jsonString); + + if (o && typeof o === "object") { + return JSON.stringify(o); + } + } + catch (e) { } + + return null; + } + + private setVaultVariable(secretName: string, secretValue: string): void { + if (!secretValue) { + return; + } + + // Encode percent explicitely as the task lib does not encode % to %AZP25 as of now. + secretName = secretName.replace(/%/g, '%AZP25'); + secretValue = secretValue.replace(/%/g, '%AZP25'); + + // Support multiple stages using different key vaults with the same secret name but with different version identifiers + let secretNameWithoutVersion = secretName.split("/")[0]; + + let doNotMaskMultilineSecrets = tl.getVariable("SYSTEM_DONOTMASKMULTILINESECRETS"); + if (doNotMaskMultilineSecrets && doNotMaskMultilineSecrets.toUpperCase() === "TRUE") { + tl.setVariable(secretNameWithoutVersion, secretValue, true); + tl.setVariable(secretName, secretValue, true); + return; + } + + if (secretValue.indexOf('\n') < 0) { + // single-line case + tl.setVariable(secretNameWithoutVersion, secretValue, true); + tl.setVariable(secretName, secretValue, true); + } + else { + // multi-line case + let strVal = this.tryFlattenJson(secretValue); + if (strVal) { + console.log(util.format("Value of secret %s has been converted to single line.", secretName)); + tl.setVariable(secretNameWithoutVersion, strVal, true); + tl.setVariable(secretName, strVal, true); + } + else { + let lines = secretValue.split('\n'); + lines.forEach((line: string, index: number) => { + this.trySetSecret(secretName, line); + }); + tl.setVariable(secretNameWithoutVersion, secretValue, true); + tl.setVariable(secretName, secretValue, true); + } + } + } + + private trySetSecret(secretName: string, secretValue: string): void { + try { + let regExp = new RegExp(secretValue); + + console.log("##vso[task.setsecret]" + secretValue); + } + catch (e) { + console.log(tl.loc("CouldNotMaskSecret", secretName)); + } + } + + private getError(error: any): any { + tl.debug(JSON.stringify(error)); + + if (error && error.message && error.statusCode && error.statusCode == 403) { + this.generateAndUploadProvisionKeyVaultPermissionsScript(); + return tl.loc("AccessDeniedError", error.message); + } + + if (error && error.message) { + return error.message; + } + + return error; + } + + private generateAndUploadProvisionKeyVaultPermissionsScript(): void { + let tempPath = tl.getVariable('Agent.BuildDirectory') || tl.getVariable('Agent.ReleaseDirectory') || process.cwd(); + let filePath = path.join(tempPath, "ProvisionKeyVaultPermissions.ps1"); + + fs.writeFile(filePath, this.provisionKeyVaultSecretsScript, (err) => { + if (err) { + console.log(tl.loc("CouldNotWriteToFile", err)); + return; + } + else { + console.log(tl.loc("UploadingAttachment", filePath)); + console.log(`##vso[task.uploadfile]${filePath}`); + } + }); + } +} \ No newline at end of file diff --git a/Tasks/AzureKeyVaultV2/operations/azure-arm-keyvault.ts b/Tasks/AzureKeyVaultV2/operations/azure-arm-keyvault.ts new file mode 100644 index 000000000000..3d91eb406f4b --- /dev/null +++ b/Tasks/AzureKeyVaultV2/operations/azure-arm-keyvault.ts @@ -0,0 +1,204 @@ +import msRestAzure = require('azure-pipelines-tasks-azure-arm-rest-v2/azure-arm-common'); +import azureServiceClient = require("azure-pipelines-tasks-azure-arm-rest-v2/AzureServiceClient"); +import azureServiceClientBase = require("azure-pipelines-tasks-azure-arm-rest-v2/AzureServiceClientBase"); +import util = require("util"); +import tl = require('azure-pipelines-task-lib/task'); +import webClient = require("azure-pipelines-tasks-azure-arm-rest-v2/webClient"); + +export class AzureKeyVaultSecret { + name: string; + enabled: boolean; + expires: Date; + contentType: string; +} + +export class KeyVaultClient extends azureServiceClient.ServiceClient { + private keyVaultName; + private keyVaultUrl; + + constructor(credentials: msRestAzure.ApplicationTokenCredentials, + subscriptionId: string, + keyVaultName: string, + keyVaultUrl: string) { + + super(credentials, subscriptionId); + + this.keyVaultName = keyVaultName; + this.keyVaultUrl = keyVaultUrl; + this.apiVersion = '2016-10-01'; + } + + public async invokeRequest(request: webClient.WebRequest): Promise { + const maxRetryCount: number = 5; + const retryIntervalInSeconds: number = 2; + const retriableErrorCodes = ["ETIMEDOUT", "ECONNRESET", "ENOTFOUND", "ESOCKETTIMEDOUT", "ECONNREFUSED", "EHOSTUNREACH", "EPIPE", "EA_AGAIN", "EAI_AGAIN"]; + const retriableStatusCodes = [408, 409, 500, 502, 503, 504]; + let timeToWait: number = retryIntervalInSeconds; + let retryCount: number = 0; + + while(true) { + try { + var response = await this.beginRequest(request); + if (response.statusCode == 401) { + var vaultResourceId = this.getValidVaultResourceId(response); + if(!!vaultResourceId) { + console.log(tl.loc("RetryingWithVaultResourceIdFromResponse", vaultResourceId)); + + this.getCredentials().activeDirectoryResourceId = vaultResourceId; // update vault resource Id + this.getCredentials().getToken(true); // Refresh authorization token in cache + var response = await this.beginRequest(request); + } + } + + if (retriableStatusCodes.indexOf(response.statusCode) != -1 && ++retryCount < maxRetryCount) { + tl.debug(util.format("Encountered a retriable status code: %s. Message: '%s'.", response.statusCode, response.statusMessage)); + await webClient.sleepFor(timeToWait); + timeToWait = timeToWait * retryIntervalInSeconds + retryIntervalInSeconds; + continue; + } + + return response; + } catch(error) { + if (retriableErrorCodes.indexOf(error.code) != -1 && ++retryCount < maxRetryCount) { + tl.debug(util.format("Encountered an error. Will retry. Error:%s. Message: %s.", error.code, error.message)); + await webClient.sleepFor(timeToWait); + timeToWait = timeToWait * retryIntervalInSeconds + retryIntervalInSeconds; + } + else { + throw error; + } + } + } + } + + public getValidVaultResourceId(response: webClient.WebResponse) { + if (!!response.headers) { + var authenticateHeader = response.headers['www-authenticate']; + if (!!authenticateHeader) { + var parsedParams = authenticateHeader.split(",").map(pair => pair.split("=").map(function(item) { + return item.trim(); + })); + + const properties = {}; + parsedParams.forEach(([key,value]) => properties[key] = value); + if(properties['resource']) { + return properties['resource'].split('"').join(''); + } + } + } + + return null; + } + + public getSecrets(nextLink: string, callback: azureServiceClientBase.ApiCallback) { + if (!callback) { + throw new Error(tl.loc("CallbackCannotBeNull")); + } + + // Create HTTP transport objects + var url = nextLink; + if (!url) + { + url = this.getRequestUriForBaseUri( + this.keyVaultUrl, + '/secrets', + {}, + ['maxresults=25']); + } + + var httpRequest = new webClient.WebRequest(); + httpRequest.method = 'GET'; + httpRequest.headers = {}; + httpRequest.uri = url; + + console.log(tl.loc("DownloadingSecretsUsing", url)); + + this.invokeRequest(httpRequest).then(async (response: webClient.WebResponse) => { + var result = []; + if (response.statusCode == 200) { + if (response.body.value) { + result = result.concat(response.body.value); + } + + if (response.body.nextLink) { + var nextResult = await this.accumulateResultFromPagedResult(response.body.nextLink); + if (nextResult.error) { + return new azureServiceClientBase.ApiResult(nextResult.error); + } + result = result.concat(nextResult.result); + + var listOfSecrets = this.convertToAzureKeyVaults(result); + return new azureServiceClientBase.ApiResult(null, listOfSecrets); + } + else { + var listOfSecrets = this.convertToAzureKeyVaults(result); + return new azureServiceClientBase.ApiResult(null, listOfSecrets); + } + } + else { + return new azureServiceClientBase.ApiResult(azureServiceClientBase.ToError(response)); + } + }).then((apiResult: azureServiceClientBase.ApiResult) => callback(apiResult.error, apiResult.result), + (error) => callback(error)); + } + + public getSecretValue(secretName: string, callback: azureServiceClientBase.ApiCallback) { + if (!callback) { + throw new Error(tl.loc("CallbackCannotBeNull")); + } + + // Create HTTP transport objects + var httpRequest = new webClient.WebRequest(); + httpRequest.method = 'GET'; + httpRequest.headers = {}; + httpRequest.uri = this.getRequestUriForBaseUri( + this.keyVaultUrl, + '/secrets/{secretName}', + { + '{secretName}': secretName + } + ); + + console.log(tl.loc("DownloadingSecretValue", secretName)); + this.invokeRequest(httpRequest).then(async (response: webClient.WebResponse) => { + if (response.statusCode == 200) { + var result = response.body.value; + return new azureServiceClientBase.ApiResult(null, result); + } + else if (response.statusCode == 400) { + return new azureServiceClientBase.ApiResult(tl.loc('GetSecretFailedBecauseOfInvalidCharacters', secretName)); + } + else { + return new azureServiceClientBase.ApiResult(azureServiceClientBase.ToError(response)); + } + }).then((apiResult: azureServiceClientBase.ApiResult) => callback(apiResult.error, apiResult.result), + (error) => callback(error)); + } + + private convertToAzureKeyVaults(result: any[]): AzureKeyVaultSecret[] { + var listOfSecrets: AzureKeyVaultSecret[] = []; + result.forEach((value: any, index: number) => { + var expires; + if (value.attributes.exp) + { + expires = new Date(0); + expires.setSeconds(parseInt(value.attributes.exp)); + } + + var secretIdentifier: string = value.id; + var lastIndex = secretIdentifier.lastIndexOf("/"); + var name: string = secretIdentifier.substr(lastIndex + 1, secretIdentifier.length); + + var azkvSecret: AzureKeyVaultSecret = { + name: name, + contentType: value.contentType, + enabled: value.attributes.enabled, + expires: expires + }; + + listOfSecrets.push(azkvSecret); + }); + + return listOfSecrets; + } +} \ No newline at end of file diff --git a/Tasks/AzureKeyVaultV2/package.json b/Tasks/AzureKeyVaultV2/package.json new file mode 100644 index 000000000000..c4c33e362da0 --- /dev/null +++ b/Tasks/AzureKeyVaultV2/package.json @@ -0,0 +1,17 @@ +{ + "name": "AzureKeyVault", + "main": "main.js", + "dependencies": { + "@types/mocha": "^5.2.7", + "@types/node": "^10.17.0", + "@types/q": "^1.5.0", + "@types/uuid": "^8.3.0", + "azure-pipelines-task-lib": "2.8.0", + "azure-pipelines-tasks-azure-arm-rest-v2": "1.0.4", + "moment": "2.21.0", + "vso-node-api": "6.0.1-preview" + }, + "devDependencies": { + "typescript": "4.0.2" + } +} diff --git a/Tasks/AzureKeyVaultV2/run.ts b/Tasks/AzureKeyVaultV2/run.ts new file mode 100644 index 000000000000..8721b1a7ee45 --- /dev/null +++ b/Tasks/AzureKeyVaultV2/run.ts @@ -0,0 +1,3 @@ +import { TaskRunner } from './task-runner'; + +TaskRunner.runInContext(false); \ No newline at end of file diff --git a/Tasks/AzureKeyVaultV2/runprejob.ts b/Tasks/AzureKeyVaultV2/runprejob.ts new file mode 100644 index 000000000000..bec04218c5b8 --- /dev/null +++ b/Tasks/AzureKeyVaultV2/runprejob.ts @@ -0,0 +1,3 @@ +import { TaskRunner } from './task-runner'; + +TaskRunner.runInContext(true); \ No newline at end of file diff --git a/Tasks/AzureKeyVaultV2/task-runner.ts b/Tasks/AzureKeyVaultV2/task-runner.ts new file mode 100644 index 000000000000..1d666a0809ab --- /dev/null +++ b/Tasks/AzureKeyVaultV2/task-runner.ts @@ -0,0 +1,50 @@ +import tl = require("azure-pipelines-task-lib/task"); +import path = require("path"); + +import keyVaultTaskParameters = require("./models/KeyVaultTaskParameters"); +import keyVault = require("./operations/KeyVault"); + +const DECODE_PERCENTS = "DECODE_PERCENTS"; + +export class TaskRunner { + public static async runInContext(isPreJobContext: boolean): Promise { + const runAsPreJob: boolean = tl.getBoolInput('RunAsPreJob', true); + if (runAsPreJob === isPreJobContext) { + this.run(); + } else { + tl.setResult(tl.TaskResult.Skipped, "Skipped according to the task's configuration. RunAsPreJob: " + runAsPreJob); + } + } + + private static async run(): Promise { + try { + const taskManifestPath = path.join(__dirname, "task.json"); + tl.debug("Setting resource path to " + taskManifestPath); + tl.setResourcePath(taskManifestPath); + + // Decode percent in agent + let decodePercents = tl.getVariable(DECODE_PERCENTS); + if (!decodePercents) { + tl.debug("Setting DECODE_PERCENTS as true to decode %AZP25 to %"); + tl.setVariable(DECODE_PERCENTS, "True", false); + } + + const secretsToErrorsMap = new keyVault.SecretsToErrorsMapping(); + const vaultParameters = new keyVaultTaskParameters.KeyVaultTaskParameters(); + const taskParameters = await vaultParameters.getKeyVaultTaskParameters(); + + const KeyVaultController = new keyVault.KeyVault(taskParameters); + await KeyVaultController.downloadSecrets(secretsToErrorsMap); + + if (!secretsToErrorsMap.isEmpty()) { + tl.setResult(tl.TaskResult.Failed, secretsToErrorsMap.getAllErrors()); + } + else { + tl.setResult(tl.TaskResult.Succeeded, ""); + } + } + catch (error) { + tl.setResult(tl.TaskResult.Failed, error); + } + } +} \ No newline at end of file diff --git a/Tasks/AzureKeyVaultV2/task.json b/Tasks/AzureKeyVaultV2/task.json new file mode 100644 index 000000000000..d1591619ac69 --- /dev/null +++ b/Tasks/AzureKeyVaultV2/task.json @@ -0,0 +1,123 @@ +{ + "id": "1E244D32-2DD4-4165-96FB-B7441CA9331E", + "name": "AzureKeyVault", + "friendlyName": "Azure Key Vault", + "description": "Download Azure Key Vault secrets", + "helpUrl": "https://docs.microsoft.com/azure/devops/pipelines/tasks/deploy/azure-key-vault", + "helpMarkDown": "[Learn more about this task](https://go.microsoft.com/fwlink/?linkid=848891)", + "category": "Deploy", + "releaseNotes": "What's new in Version 2.0:
 Added support for %3B, %5D in secrets", + "visibility": [ + "Build", + "Release" + ], + "author": "Microsoft Corporation", + "version": { + "Major": 2, + "Minor": 186, + "Patch": 0 + }, + "demands": [], + "minimumAgentVersion": "2.182.1", + "inputs": [ + { + "name": "ConnectedServiceName", + "aliases": [ + "azureSubscription" + ], + "type": "connectedService:AzureRM", + "label": "Azure subscription", + "defaultValue": "", + "required": true, + "helpMarkDown": "Select the Azure subscription for the key vault" + }, + { + "name": "KeyVaultName", + "type": "pickList", + "label": "Key vault", + "required": true, + "helpMarkDown": "Provide the name of an existing key vault", + "properties": { + "EditableOptions": "True" + } + }, + { + "name": "SecretsFilter", + "type": "string", + "label": "Secrets filter", + "defaultValue": "*", + "required": true, + "helpMarkDown": "Comma separated list of secret names or leave * to download all secrets from the selected key vault.", + "options": { + "EditableOptions": "True" + } + }, + { + "name": "RunAsPreJob", + "type": "boolean", + "label": "Make secrets available to whole job", + "defaultValue": false, + "required": true, + "helpMarkDown": "Run the task before job execution begins. Exposes secrets to all tasks in the job, not just tasks that follow this one.", + "options": { + "EditableOptions": "True" + } + } + ], + "dataSourceBindings": [ + { + "target": "KeyVaultName", + "endpointId": "$(ConnectedServiceName)", + "dataSourceName": "AzureKeyVaultsListV2", + "resultTemplate": "{ \"Value\" : \"{{{name}}}\", \"DisplayValue\" : \"{{{name}}}\" }" + } + ], + "instanceNameFormat": "Azure Key Vault: $(KeyVaultName)", + "prejobexecution": { + "Node10": { + "target": "runprejob.js" + } + }, + "execution": { + "Node10": { + "target": "run.js" + } + }, + "messages": { + "ClientIdCannotBeEmpty": "clientId must be a non empty string.", + "DomainCannotBeEmpty": "domain must be a non empty string.", + "SecretCannotBeEmpty": "secret must be a non empty string.", + "armUrlCannotBeEmpty": "arm URL must be a non empty string.", + "authorityUrlCannotBeEmpty": "authority must be a non empty string.", + "CallbackCannotBeNull": "callback cannot be null.", + "CredentialsCannotBeNull": "'credentials' cannot be null.", + "SubscriptionIdCannotBeNull": "'subscriptionId' cannot be null.", + "InvalidResponseLongRunningOperation": "Invalid response received for fetching status of a long running operation.", + "TimeoutWhileWaiting": "Timed out while waiting", + "ResourceGroupCannotBeNull": "resourceGroupName cannot be null or undefined and it must be of type string.", + "ResourceGroupExceededLength": "\"resourceGroupName\" should satisfy the constraint - \"MaxLength\": 90", + "ResourceGroupDeceededLength": "\"resourceGroupName\" should satisfy the constraint - \"MinLength\": 1", + "ResourceGroupDoesntMatchPattern": "\"resourceGroupName\" should satisfy the constraint - \"Pattern\": /^[-\\w\\._\\(\\)]+$/", + "AzKv_ConstructorFailed": "Azure key vault parameters initialization failed. Error: %s.", + "SubscriptionIdLabel": "SubscriptionId: %s.", + "KeyVaultNameLabel": "Key vault name: %s.", + "DownloadingSecretsUsing": "Downloading secrets using: %s.", + "GetSecretsFailed": "Get secrets failed. Error: %s.", + "NoSecretsFound": "No secrets found in: %s", + "NumberOfSecretsFound": "Number of secrets found in %s: %s", + "NumberOfEnabledSecretsFound": "Number of enabled and unexpired secrets found in %s: %s", + "DownloadingSecretValue": "Downloading secret value for: %s.", + "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.", + "GetSecretValueFailed": "Get secret value failed for: %s. Error: %s.", + "ConflictingVariableFound": "Variable with name %s is defined in both environment and key vault", + "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.", + "UploadingAttachment": "Uploading %s as attachment", + "CouldNotWriteToFile": "Could not save content to file. Failed with an error %s", + "CouldNotMaskSecret": "%s value has regular expressions hence could not mask completely", + "CouldNotFetchAccessTokenforAzureStatusCode": "Could not fetch access token for Azure. Status code: %s, status message: %s", + "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", + "CouldNotFetchAccessTokenforMSIStatusCode": "Could not fetch access token for Managed Service Principal. Status code: %s, status message: %s", + "RetryingWithVaultResourceIdFromResponse": "Retrying with vault resource Id reterieved from response : %s", + "ExpiredServicePrincipal": "Could not fetch access token for Azure. Verify if the Service Principal used is valid and not expired." + } +} \ No newline at end of file diff --git a/Tasks/AzureKeyVaultV2/task.loc.json b/Tasks/AzureKeyVaultV2/task.loc.json new file mode 100644 index 000000000000..5226b213c943 --- /dev/null +++ b/Tasks/AzureKeyVaultV2/task.loc.json @@ -0,0 +1,123 @@ +{ + "id": "1E244D32-2DD4-4165-96FB-B7441CA9331E", + "name": "AzureKeyVault", + "friendlyName": "ms-resource:loc.friendlyName", + "description": "ms-resource:loc.description", + "helpUrl": "https://docs.microsoft.com/azure/devops/pipelines/tasks/deploy/azure-key-vault", + "helpMarkDown": "ms-resource:loc.helpMarkDown", + "category": "Deploy", + "releaseNotes": "ms-resource:loc.releaseNotes", + "visibility": [ + "Build", + "Release" + ], + "author": "Microsoft Corporation", + "version": { + "Major": 2, + "Minor": 186, + "Patch": 0 + }, + "demands": [], + "minimumAgentVersion": "2.182.1", + "inputs": [ + { + "name": "ConnectedServiceName", + "aliases": [ + "azureSubscription" + ], + "type": "connectedService:AzureRM", + "label": "ms-resource:loc.input.label.ConnectedServiceName", + "defaultValue": "", + "required": true, + "helpMarkDown": "ms-resource:loc.input.help.ConnectedServiceName" + }, + { + "name": "KeyVaultName", + "type": "pickList", + "label": "ms-resource:loc.input.label.KeyVaultName", + "required": true, + "helpMarkDown": "ms-resource:loc.input.help.KeyVaultName", + "properties": { + "EditableOptions": "True" + } + }, + { + "name": "SecretsFilter", + "type": "string", + "label": "ms-resource:loc.input.label.SecretsFilter", + "defaultValue": "*", + "required": true, + "helpMarkDown": "ms-resource:loc.input.help.SecretsFilter", + "options": { + "EditableOptions": "True" + } + }, + { + "name": "RunAsPreJob", + "type": "boolean", + "label": "ms-resource:loc.input.label.RunAsPreJob", + "defaultValue": false, + "required": true, + "helpMarkDown": "ms-resource:loc.input.help.RunAsPreJob", + "options": { + "EditableOptions": "True" + } + } + ], + "dataSourceBindings": [ + { + "target": "KeyVaultName", + "endpointId": "$(ConnectedServiceName)", + "dataSourceName": "AzureKeyVaultsListV2", + "resultTemplate": "{ \"Value\" : \"{{{name}}}\", \"DisplayValue\" : \"{{{name}}}\" }" + } + ], + "instanceNameFormat": "ms-resource:loc.instanceNameFormat", + "prejobexecution": { + "Node10": { + "target": "runprejob.js" + } + }, + "execution": { + "Node10": { + "target": "run.js" + } + }, + "messages": { + "ClientIdCannotBeEmpty": "ms-resource:loc.messages.ClientIdCannotBeEmpty", + "DomainCannotBeEmpty": "ms-resource:loc.messages.DomainCannotBeEmpty", + "SecretCannotBeEmpty": "ms-resource:loc.messages.SecretCannotBeEmpty", + "armUrlCannotBeEmpty": "ms-resource:loc.messages.armUrlCannotBeEmpty", + "authorityUrlCannotBeEmpty": "ms-resource:loc.messages.authorityUrlCannotBeEmpty", + "CallbackCannotBeNull": "ms-resource:loc.messages.CallbackCannotBeNull", + "CredentialsCannotBeNull": "ms-resource:loc.messages.CredentialsCannotBeNull", + "SubscriptionIdCannotBeNull": "ms-resource:loc.messages.SubscriptionIdCannotBeNull", + "InvalidResponseLongRunningOperation": "ms-resource:loc.messages.InvalidResponseLongRunningOperation", + "TimeoutWhileWaiting": "ms-resource:loc.messages.TimeoutWhileWaiting", + "ResourceGroupCannotBeNull": "ms-resource:loc.messages.ResourceGroupCannotBeNull", + "ResourceGroupExceededLength": "ms-resource:loc.messages.ResourceGroupExceededLength", + "ResourceGroupDeceededLength": "ms-resource:loc.messages.ResourceGroupDeceededLength", + "ResourceGroupDoesntMatchPattern": "ms-resource:loc.messages.ResourceGroupDoesntMatchPattern", + "AzKv_ConstructorFailed": "ms-resource:loc.messages.AzKv_ConstructorFailed", + "SubscriptionIdLabel": "ms-resource:loc.messages.SubscriptionIdLabel", + "KeyVaultNameLabel": "ms-resource:loc.messages.KeyVaultNameLabel", + "DownloadingSecretsUsing": "ms-resource:loc.messages.DownloadingSecretsUsing", + "GetSecretsFailed": "ms-resource:loc.messages.GetSecretsFailed", + "NoSecretsFound": "ms-resource:loc.messages.NoSecretsFound", + "NumberOfSecretsFound": "ms-resource:loc.messages.NumberOfSecretsFound", + "NumberOfEnabledSecretsFound": "ms-resource:loc.messages.NumberOfEnabledSecretsFound", + "DownloadingSecretValue": "ms-resource:loc.messages.DownloadingSecretValue", + "AccessDeniedError": "ms-resource:loc.messages.AccessDeniedError", + "GetSecretValueFailed": "ms-resource:loc.messages.GetSecretValueFailed", + "ConflictingVariableFound": "ms-resource:loc.messages.ConflictingVariableFound", + "GetSecretFailedBecauseOfInvalidCharacters": "ms-resource:loc.messages.GetSecretFailedBecauseOfInvalidCharacters", + "UploadingAttachment": "ms-resource:loc.messages.UploadingAttachment", + "CouldNotWriteToFile": "ms-resource:loc.messages.CouldNotWriteToFile", + "CouldNotMaskSecret": "ms-resource:loc.messages.CouldNotMaskSecret", + "CouldNotFetchAccessTokenforAzureStatusCode": "ms-resource:loc.messages.CouldNotFetchAccessTokenforAzureStatusCode", + "CouldNotFetchAccessTokenforMSIDueToMSINotConfiguredProperlyStatusCode": "ms-resource:loc.messages.CouldNotFetchAccessTokenforMSIDueToMSINotConfiguredProperlyStatusCode", + "CouldNotFetchAccessTokenforMSIStatusCode": "ms-resource:loc.messages.CouldNotFetchAccessTokenforMSIStatusCode", + "RetryingWithVaultResourceIdFromResponse": "ms-resource:loc.messages.RetryingWithVaultResourceIdFromResponse", + "ExpiredServicePrincipal": "ms-resource:loc.messages.ExpiredServicePrincipal" + } +} \ No newline at end of file diff --git a/Tasks/AzureKeyVaultV2/tsconfig.json b/Tasks/AzureKeyVaultV2/tsconfig.json new file mode 100644 index 000000000000..af65b026d426 --- /dev/null +++ b/Tasks/AzureKeyVaultV2/tsconfig.json @@ -0,0 +1,7 @@ +{ + "compilerOptions": { + "target": "ES6", + "module": "commonjs", + "types": ["node"] + } +} diff --git a/make-options.json b/make-options.json index 2806c6d33722..647736b7eaf9 100644 --- a/make-options.json +++ b/make-options.json @@ -23,6 +23,7 @@ "AzureFunctionV1", "AzureIoTEdgeV2", "AzureKeyVaultV1", + "AzureKeyVaultV2", "AzureMonitorAlertsV0", "AzureMonitorV0", "AzureMonitorV1",