mirror of
https://github.com/azure/login.git
synced 2026-03-15 09:20:56 -04:00
Compare commits
8 Commits
v1.4.5
...
N-Usha-pat
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3bcabb32ed | ||
|
|
8e3c83b515 | ||
|
|
ceaa639e34 | ||
|
|
0a49ca330b | ||
|
|
66ade64420 | ||
|
|
5642aae9c6 | ||
|
|
b609339187 | ||
|
|
a4b1865541 |
645
README.md
645
README.md
@@ -1,317 +1,328 @@
|
|||||||
# GitHub Actions for deploying to Azure
|
# GitHub Actions for deploying to Azure
|
||||||
|
|
||||||
## Automate your GitHub workflows using Azure Actions
|
## Automate your GitHub workflows using Azure Actions
|
||||||
|
|
||||||
[GitHub Actions](https://help.github.com/en/articles/about-github-actions) gives you the flexibility to build an automated software development lifecycle workflow.
|
[GitHub Actions](https://help.github.com/en/articles/about-github-actions) gives you the flexibility to build an automated software development lifecycle workflow.
|
||||||
|
|
||||||
With [GitHub Actions for Azure](https://github.com/Azure/actions/) you can create workflows that you can set up in your repository to build, test, package, release and **deploy** to Azure.
|
With [GitHub Actions for Azure](https://github.com/Azure/actions/) you can create workflows that you can set up in your repository to build, test, package, release and **deploy** to Azure.
|
||||||
|
|
||||||
# GitHub Action for Azure Login
|
NOTE: you must have write permissions to the repository in question. If you're using a sample repository from Microsoft, be sure to first fork the repository to your own GitHub account.
|
||||||
|
|
||||||
With the [Azure Login](https://github.com/Azure/login/blob/master/action.yml) Action, you can automate your workflow to do an Azure login using [Azure service principal](https://docs.microsoft.com/en-us/azure/active-directory/develop/app-objects-and-service-principals) and run Az CLI and Azure PowerShell scripts.
|
Get started today with a [free Azure account](https://azure.com/free/open-source).
|
||||||
|
|
||||||
- By default, the action only logs in with the Azure CLI (using the `az login` command). To log in with the Az PowerShell module, set `enable-AzPSSession` to true. To login to Azure tenants without any subscriptions, set the optional parameter `allow-no-subscriptions` to true.
|
# GitHub Action for Azure Login
|
||||||
|
|
||||||
- To login into one of the Azure Government clouds or Azure Stack, set the optional parameter `environment` with one of the supported values `AzureUSGovernment` or `AzureChinaCloud` or `AzureStack`. If this parameter is not specified, it takes the default value `AzureCloud` and connects to the Azure Public Cloud. Additionally the parameter `creds` takes the Azure service principal created in the particular cloud to connect (Refer to [this](#configure-a-service-principal-with-a-secret) section below for details).
|
With the Azure login Action, you can automate your workflow to do an Azure login using [Azure service principal](https://docs.microsoft.com/azure/active-directory/develop/app-objects-and-service-principals) and run Azure CLI and Azure PowerShell scripts. You can leverage this action for the public or soverign clouds including Azure Government and Azure Stack Hub (using the `environment` parameter).
|
||||||
|
|
||||||
- The Action supports two different ways of authentication with Azure. One using the Azure Service Principal with secrets. The other is OpenID connect (OIDC) method of authentication using Azure Service Principal with a Federated Identity Credential.
|
By default, the action only logs in with the Azure CLI (using the `az login` command). To log in with the Az PowerShell module, set `enable-AzPSSession` to true. To login to Azure tenants without any subscriptions, set the optional parameter `allow-no-subscriptions` to true.
|
||||||
- To login using Azure Service Principal with a secret, follow [this](#configure-a-service-principal-with-a-secret) guidance.
|
|
||||||
- To login using **OpenID Connect (OIDC) based Federated Identity Credentials**,
|
To login into one of the Azure Government clouds, set the optional parameter environment with supported cloud names AzureUSGovernment or AzureChinaCloud. If this parameter is not specified, it takes the default value AzureCloud and connect to the Azure Public Cloud. Additionally the parameter creds takes the Azure service principal created in the particular cloud to connect (Refer to Configure deployment credentials section below for details).
|
||||||
1. Follow [this](#configure-a-service-principal-with-a-federated-credential-to-use-oidc-based-authentication) guidance to create a Federated Credential associated with your AD App (Service Principal). This is needed to establish OIDC trust between GitHub deployment workflows and the specific Azure resources scoped by the service principal.
|
|
||||||
2. In your GitHub workflow, Set `permissions:` with `id-token: write` at workflow level or job level based on whether the OIDC token needs to be auto-generated for all Jobs or a specific Job.
|
To login using **Open ID Connect (OIDC) based federated identity credentials**, set the `client-id`, `tenant-id` and `subscription-id` of the Azure service principal associated with an OIDC Federated Identity Credential.
|
||||||
3. Within the Job deploying to Azure, add Azure/login action and pass the `client-id`, `tenant-id` and `subscription-id` of the Azure service principal associated with an OIDC Federated Identity Credential credeted in step (i)
|
|
||||||
|
Follow <this> guidance, to create a new service principal and then to create a Federated credential in Azure portal needed to establish OIDC trust between GitHub deployment workflows and the specific Azure resources scoped by the service principal. Configure the Federated Credential with appropriate values of the GitHub Org, Repo and Environments based on the context used in the GitHub deployment workflows targeting Azure.
|
||||||
Note:
|
|
||||||
- Ensure the CLI version is 2.30 or above to use OIDC support.
|
Note: Currently OIDC login is supported only with Azure CLI for public clouds. Support for Azure PowerShell and for other clouds like Government clouds, Azure Stacks would be added soon.
|
||||||
- OIDC support in Azure is supported only for public clouds. Support for other clouds like Government clouds, Azure Stacks would be added soon.
|
|
||||||
- By default, Azure access tokens issued during OIDC based login could have limited validity. This expiration time is configurable in Azure.
|
This repository contains GitHub Action for [Azure Login](https://github.com/Azure/login/blob/master/action.yml).
|
||||||
|
|
||||||
## Sample workflow that uses Azure login action to run az cli
|
## Sample workflow that uses Azure login action to run az cli
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
|
# File: .github/workflows/workflow.yml
|
||||||
# File: .github/workflows/workflow.yml
|
|
||||||
|
on: [push]
|
||||||
on: [push]
|
|
||||||
|
name: AzureLoginSample
|
||||||
name: AzureLoginSample
|
|
||||||
|
jobs:
|
||||||
jobs:
|
|
||||||
|
build-and-deploy:
|
||||||
build-and-deploy:
|
runs-on: ubuntu-latest
|
||||||
runs-on: ubuntu-latest
|
steps:
|
||||||
steps:
|
|
||||||
|
- uses: azure/login@v1
|
||||||
- uses: azure/login@v1
|
with:
|
||||||
with:
|
creds: ${{ secrets.AZURE_CREDENTIALS }}
|
||||||
creds: ${{ secrets.AZURE_CREDENTIALS }}
|
|
||||||
|
- run: |
|
||||||
- run: |
|
az webapp list --query "[?state=='Running']"
|
||||||
az webapp list --query "[?state=='Running']"
|
```
|
||||||
|
|
||||||
```
|
## Sample workflow that uses Azure login action using OIDC/Federated Identity credentials to run az cli
|
||||||
|
|
||||||
## Sample workflow that uses Azure login action to run Azure PowerShell
|
```yaml
|
||||||
|
# File: .github/workflows/OIDC_workflow.yml
|
||||||
```yaml
|
|
||||||
|
name: Run Azure Login with OIDC
|
||||||
# File: .github/workflows/workflow.yml
|
on: [push]
|
||||||
|
|
||||||
on: [push]
|
permissions:
|
||||||
|
id-token: write
|
||||||
name: AzurePowerShellLoginSample
|
contents: write
|
||||||
|
jobs:
|
||||||
jobs:
|
build-and-deploy:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
build:
|
steps:
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
- name: Installing CLI-beta for OIDC
|
||||||
|
run: |
|
||||||
- name: Login via Az module
|
CWD="$(pwd)"
|
||||||
uses: azure/login@v1
|
python3 -m venv oidc-venv
|
||||||
with:
|
. oidc-venv/bin/activate
|
||||||
creds: ${{secrets.AZURE_CREDENTIALS}}
|
echo "activated environment"
|
||||||
enable-AzPSSession: true
|
python3 -m pip install -q --upgrade pip
|
||||||
|
echo "started installing cli beta"
|
||||||
- run: |
|
pip install -q --extra-index-url https://azurecliedge.blob.core.windows.net/federated-token/simple/ azure-cli
|
||||||
Get-AzVM -ResourceGroupName "ResourceGroup11"
|
echo "***************installed cli beta*******************"
|
||||||
|
echo "$CWD/oidc-venv/bin" >> $GITHUB_PATH
|
||||||
```
|
|
||||||
## Sample workflow that uses Azure login action using OIDC to run az cli (Linux)
|
- name: 'Az CLI login'
|
||||||
|
uses: azure/login@oidc-support
|
||||||
```yaml
|
with:
|
||||||
# File: .github/workflows/OIDC_workflow.yml
|
client-id: ${{ secrets.AZURE_CLIENTID }}
|
||||||
|
tenant-id: ${{ secrets.AZURE_TENANTID }}
|
||||||
name: Run Azure Login with OIDC
|
subscription-id: ${{ secrets.AZURE_SUBSCRIPTIONID }}
|
||||||
on: [push]
|
|
||||||
|
- name: 'Run az commands'
|
||||||
permissions:
|
run: |
|
||||||
id-token: write
|
az account show
|
||||||
contents: read
|
az group list
|
||||||
jobs:
|
pwd
|
||||||
build-and-deploy:
|
```
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
## Sample workflow that uses Azure login action to run Azure PowerShell
|
||||||
- name: 'Az CLI login'
|
|
||||||
uses: azure/login@v1
|
```yaml
|
||||||
with:
|
# File: .github/workflows/workflow.yml
|
||||||
client-id: ${{ secrets.AZURE_CLIENT_ID }}
|
|
||||||
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
|
on: [push]
|
||||||
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
|
|
||||||
|
name: AzurePowerShellSample
|
||||||
- name: 'Run az commands'
|
|
||||||
run: |
|
jobs:
|
||||||
az account show
|
|
||||||
az group list
|
build-and-deploy:
|
||||||
pwd
|
runs-on: ubuntu-latest
|
||||||
```
|
steps:
|
||||||
Users can also specify `audience` field for access-token in the input parameters of the action. If not specified, it is defaulted to `api://AzureADTokenExchange`. This action supports login az powershell as well for both windows and linux runners by setting an input parameter `enable-AzPSSession: true`. Below is the sample workflow for the same using the windows runner. Please note that powershell login is not supported in Macos runners.
|
|
||||||
|
- name: Login via Az module
|
||||||
## Sample workflow that uses Azure login action using OIDC to run az PowerShell (Windows)
|
uses: azure/login@v1
|
||||||
|
with:
|
||||||
```yaml
|
creds: ${{secrets.AZURE_CREDENTIALS}}
|
||||||
# File: .github/workflows/OIDC_workflow.yml
|
enable-AzPSSession: true
|
||||||
|
|
||||||
name: Run Azure Login with OIDC
|
- name: Run Az CLI script
|
||||||
on: [push]
|
run: |
|
||||||
|
az webapp list --query "[?state=='Running']"
|
||||||
permissions:
|
|
||||||
id-token: write
|
- name: Run Azure PowerShell script
|
||||||
contents: read
|
uses: azure/powershell@v1
|
||||||
|
with:
|
||||||
jobs:
|
azPSVersion: '3.1.0'
|
||||||
Windows-latest:
|
inlineScript: |
|
||||||
runs-on: windows-latest
|
Get-AzVM -ResourceGroupName "ActionsDemo"
|
||||||
steps:
|
```
|
||||||
- name: OIDC Login to Azure Public Cloud with AzPowershell (enableAzPSSession true)
|
|
||||||
uses: azure/login@v1
|
## Sample to connect to Azure US Government cloud
|
||||||
with:
|
|
||||||
client-id: ${{ secrets.AZURE_CLIENT_ID }}
|
```yaml
|
||||||
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
|
- name: Login to Azure US Gov Cloud with CLI
|
||||||
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
|
uses: azure/login@v1
|
||||||
enable-AzPSSession: true
|
with:
|
||||||
|
creds: ${{ secrets.AZURE_US_GOV_CREDENTIALS }}
|
||||||
- name: 'Get RG with powershell action'
|
environment: 'AzureUSGovernment'
|
||||||
uses: azure/powershell@v1
|
enable-AzPSSession: false
|
||||||
with:
|
- name: Login to Azure US Gov Cloud with Az Powershell
|
||||||
inlineScript: |
|
uses: azure/login@v1
|
||||||
Get-AzResourceGroup
|
with:
|
||||||
azPSVersion: "latest"
|
creds: ${{ secrets.AZURE_US_GOV_CREDENTIALS }}
|
||||||
|
environment: 'AzureUSGovernment'
|
||||||
```
|
enable-AzPSSession: true
|
||||||
|
```
|
||||||
Refer [Azure PowerShell](https://github.com/azure/powershell) Github action to run your Azure PowerShell scripts.
|
|
||||||
|
Refer to the [Azure PowerShell](https://github.com/azure/powershell) Github action to run your Azure PowerShell scripts.
|
||||||
## Sample to connect to Azure US Government cloud
|
|
||||||
|
## Sample Azure Login workflow that to run az cli on Azure Stack Hub
|
||||||
```yaml
|
|
||||||
- name: Login to Azure US Gov Cloud with CLI
|
```yaml
|
||||||
uses: azure/login@v1
|
|
||||||
with:
|
# File: .github/workflows/workflow.yml
|
||||||
creds: ${{ secrets.AZURE_US_GOV_CREDENTIALS }}
|
|
||||||
environment: 'AzureUSGovernment'
|
on: [push]
|
||||||
enable-AzPSSession: false
|
|
||||||
- name: Login to Azure US Gov Cloud with Az Powershell
|
name: AzureLoginSample
|
||||||
uses: azure/login@v1
|
|
||||||
with:
|
jobs:
|
||||||
creds: ${{ secrets.AZURE_US_GOV_CREDENTIALS }}
|
|
||||||
environment: 'AzureUSGovernment'
|
build-and-deploy:
|
||||||
enable-AzPSSession: true
|
runs-on: ubuntu-latest
|
||||||
```
|
steps:
|
||||||
|
- uses: azure/login@v1
|
||||||
Refer to the [Azure PowerShell](https://github.com/azure/powershell) Github action to run your Azure PowerShell scripts.
|
with:
|
||||||
|
creds: ${{ secrets.AZURE_CREDENTIALS }}
|
||||||
## Sample Azure Login workflow that to run az cli on Azure Stack Hub
|
environment: 'AzureStack'
|
||||||
|
|
||||||
```yaml
|
- run: |
|
||||||
|
az webapp list --query "[?state=='Running']"
|
||||||
# File: .github/workflows/workflow.yml
|
|
||||||
|
```
|
||||||
on: [push]
|
Refer to the [Azure Stack Hub Login Action Tutorial](https://docs.microsoft.com/en-us/azure-stack/user/ci-cd-github-action-login-cli?view=azs-2008) for more detailed instructions.
|
||||||
|
|
||||||
name: AzureLoginSample
|
## Configure deployment credentials:
|
||||||
|
|
||||||
jobs:
|
|
||||||
|
### Configure OIDC federated credentials:
|
||||||
build-and-deploy:
|
|
||||||
runs-on: ubuntu-latest
|
To login using **Open ID Connect (OIDC) based federated identity credentials**, in the workflow, set the values of `client-id`, `tenant-id` and `subscription-id` of the Azure service principal associated with an OIDC Federated Identity Credential as individual repository secrets.
|
||||||
steps:
|
|
||||||
- uses: azure/login@v1
|
Follow <this> guidance, to create a new service principal and then to create a Federated credential in Azure portal needed to establish OIDC trust between GitHub deployment workflows and the specific Azure resources scoped by the service principal. Configure the Federated Credential with appropriate values of the GitHub Org, Repo and Environments based on the context used in the GitHub deployment workflows targeting Azure.
|
||||||
with:
|
|
||||||
creds: ${{ secrets.AZURE_CREDENTIALS }}
|
Note: Currently OIDC login is supported only with Azure CLI for public clouds. Support for Azure PowerShell and for other clouds like Government clouds, Azure Stacks would be added soon. Inorder to login in that case you need to use the approach as shown below for configuring credentials for using non-OIDC login.
|
||||||
environment: 'AzureStack'
|
|
||||||
|
### Configure non-OIDC credentials:
|
||||||
- run: |
|
|
||||||
az webapp list --query "[?state=='Running']"
|
If the credentials are supplied as as a single JSON object secret then the login action will use non-OIDC approach. Azure login Action in this case depends on a [secret](https://docs.github.com/en/free-pro-team@latest/actions/reference/encrypted-secrets) named `AZURE_CREDENTIALS` in your repository. The value of this secret is expected to be a JSON object that represents a service principal (an identifer for an application or process) that authenticates the workflow with Azure.
|
||||||
|
|
||||||
```
|
To function correctly, this service principal must be assigned the [Contributor]((https://docs.microsoft.com/azure/role-based-access-control/built-in-roles#contributor)) role for the web app or the resource group that contains the web app.
|
||||||
Refer to the [Azure Stack Hub Login Action Tutorial](https://docs.microsoft.com/en-us/azure-stack/user/ci-cd-github-action-login-cli?view=azs-2008) for more detailed instructions.
|
The following steps describe how to create the service principal, assign the role, and create a single secret in your repository with the resulting credentials.
|
||||||
|
|
||||||
## Configure deployment credentials:
|
1. Open the Azure Cloud Shell at [https://shell.azure.com](https://shell.azure.com). You can alternately use the [Azure CLI](https://docs.microsoft.com/cli/azure/install-azure-cli?view=azure-cli-latest) if you've installed it locally. (For more information on Cloud Shell, see the [Cloud Shell Overview](https://docs.microsoft.com/azure/cloud-shell/overview).)
|
||||||
|
|
||||||
### Configure a service principal with a secret:
|
1.1 **(Required ONLY when environment is Azure Stack Hub)** Run the following command to set the SQL Management endpoint to 'not supported'
|
||||||
|
```bash
|
||||||
For using any credentials like Azure Service Principal, Publish Profile etc add them as [secrets](https://help.github.com/en/articles/virtual-environments-for-github-actions#creating-and-using-secrets-encrypted-variables) in the GitHub repository and then use them in the workflow.
|
|
||||||
|
az cloud update -n {environmentName} --endpoint-sql-management https://notsupported
|
||||||
|
|
||||||
Follow the steps to configure Azure Service Principal with a secret:
|
```
|
||||||
* Define a new secret under your repository settings, Add secret menu
|
|
||||||
* Store the output of the below [az cli](https://docs.microsoft.com/en-us/cli/azure/?view=azure-cli-latest) command as the value of secret variable, for example 'AZURE_CREDENTIALS'
|
2. Use the [az ad sp create-for-rbac](https://docs.microsoft.com/cli/azure/ad/sp?view=azure-cli-latest#az_ad_sp_create_for_rbac) command to create a service principal and assign a Contributor role:
|
||||||
```bash
|
|
||||||
|
For web apps (also more secure)
|
||||||
az ad sp create-for-rbac --name "myApp" --role contributor \
|
|
||||||
--scopes /subscriptions/{subscription-id}/resourceGroups/{resource-group} \
|
```azurecli
|
||||||
--sdk-auth
|
az ad sp create-for-rbac --name "{sp-name}" --sdk-auth --role contributor \
|
||||||
|
--scopes /subscriptions/{subscription-id}/resourceGroups/{resource-group}/providers/Microsoft.Web/sites/{app-name}
|
||||||
# Replace {subscription-id}, {resource-group} with the subscription, resource group details
|
```
|
||||||
|
|
||||||
# The command should output a JSON object similar to this:
|
For usage with other Azure services (Storage Accounts, Active Directory, etc.)
|
||||||
|
|
||||||
|
```azurecli
|
||||||
{
|
az ad sp create-for-rbac --name "{sp-name}" --sdk-auth --role contributor \
|
||||||
"clientId": "<GUID>",
|
--scopes /subscriptions/{subscription-id}/resourceGroups/{resource-group}
|
||||||
"clientSecret": "<STRING>",
|
```
|
||||||
"subscriptionId": "<GUID>",
|
|
||||||
"tenantId": "<GUID>",
|
Replace the following:
|
||||||
"resourceManagerEndpointUrl": "<URL>"
|
* `{sp-name}` with a suitable name for your service principal, such as the name of the app itself. The name must be unique within your organization.
|
||||||
(...)
|
* `{subscription-id}` with the subscription ID you want to use (found in Subscriptions in portal)
|
||||||
}
|
* `{resource-group}` the resource group containing the web app.
|
||||||
|
* [optional] `{app-name}` if you wish to have a tighter & more secure scope, use the first option and replace this with the name of the web app.
|
||||||
```
|
|
||||||
* Now in the workflow file in your branch: `.github/workflows/workflow.yml` replace the secret in Azure login action with your secret (Refer to the example above)
|
More info can be found [here](https://docs.microsoft.com/en-us/cli/azure/ad/sp?view=azure-cli-latest#az_ad_sp_create_for_rbac).
|
||||||
* Note: The above `az ad sp create-for-rbac` command will give you the `--sdk-auth` deprecation warning. As we are working with CLI for this deprecation process, we strongly recommend users to use this `--sdk-auth` flag as the result dictionary output changes and not accepted by login action if `--sdk-auth` is not used.
|
|
||||||
|
This command invokes Azure Active Directory (via the `ad` part of the command) to create a service principal (via `sp`) specifically for [Role-Based Access Control (RBAC)](https://docs.microsoft.com/azure/role-based-access-control/overview) (via `create-for-rbac`).
|
||||||
### Manually creating the Credentials object
|
|
||||||
|
The `--role` argument specifies the permissions to grant to the service principal at the specified `--scope`. In this case, you grant the built-in [Contributor](https://docs.microsoft.com/azure/role-based-access-control/built-in-roles#contributor) role at the scope of the web app in the specified resource group in the specified subscription. If desired, you can omit the part of the scope starting with `/providers/...` to grant the service principal the Contributor role for the entire resource group. For security purposes, however, it's always preferable to grant permissions at the most restrictive scope possible.
|
||||||
If you already created and assigned a Service Principal in Azure you can manually create the .json object above by finding the `clientId` and `clientSecret` on the Service Principal, and your `subscriptionId` and `tenantId` of the subscription and tenant respectively. The `resourceManagerEndpointUrl` will be `https://management.azure.com/` if you are using the public Azure cloud.
|
|
||||||
|
3. When complete, the `az ad sp create-for-rbac` command displays JSON output in the following form (which is specified by the `--sdk-auth` argument):
|
||||||
### Configure a service principal with a Federated Credential to use OIDC based authentication:
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
You can add federated credentials in the Azure portal or with the Microsoft Graph REST API.
|
"clientId": "<GUID>",
|
||||||
|
"clientSecret": "<CLIENT_SECRET_VALUE>",
|
||||||
#### Azure portal
|
"subscriptionId": "<GUID>",
|
||||||
1. [Register an application](https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-register-app) in Azure Portal
|
"tenantId": "<GUID>",
|
||||||
2. Within the registered application, Go to **Certificates & secrets**.
|
(...)
|
||||||
3. In the **Federated credentials** tab, select **Add credential**.
|
}
|
||||||
4. The **Add a credential** blade opens.
|
```
|
||||||
5. In the **Federated credential scenario** box select **GitHub actions deploying Azure resources**.
|
|
||||||
6. Specify the **Organization** and **Repository** for your GitHub Actions workflow which needs to access the Azure resources scoped by this App (Service Principal)
|
4. In your repository, use **Add secret** to create a new secret named `AZURE_CREDENTIALS` (as shown in the example workflow), or using whatever name is in your workflow file.
|
||||||
7. For **Entity type**, select **Environment**, **Branch**, **Pull request**, or **Tag** and specify the value, based on how you have configured the trigger for your GitHub workflow. For a more detailed overview, see [GitHub OIDC guidance]( https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect#defining-[…]dc-claims).
|
|
||||||
8. Add a **Name** for the federated credential.
|
NOTE: While adding secret `AZURE_CREDENTIALS` make sure to add like this
|
||||||
9. Click **Add** to configure the federated credential.
|
|
||||||
10. Make sure the above created application has the `contributor` access to the provided subscription. Visit [role-based-access-control](https://docs.microsoft.com/en-us/azure/role-based-access-control/role-assignments-portal?tabs=current#prerequisites) for more details.
|
{"clientId": "<GUID>",
|
||||||
|
"clientSecret": "<CLIENT_SECRET_VALUE>",
|
||||||
For a more detailed overview, see more guidance around [Azure Federated Credentials](https://docs.microsoft.com/en-us/azure/active-directory/develop/workload-identity-federation-create-trust-github).
|
"subscriptionId": "<GUID>",
|
||||||
|
"tenantId": "<GUID>",
|
||||||
#### Microsoft Graph
|
(...)}
|
||||||
|
|
||||||
1. Launch [Azure Cloud Shell](https://portal.azure.com/#cloudshell/) and sign in to your tenant.
|
instead of
|
||||||
1. Create a federated identity credential
|
|
||||||
|
{
|
||||||
Run the following command to [create a new federated identity credential](https://docs.microsoft.com/en-us/graph/api/application-post-federatedidentitycredentials?view=graph-rest-beta&preserve-view=true) on your app (specified by the object ID of the app). Substitute the values `APPLICATION-OBJECT-ID`, `CREDENTIAL-NAME`, `SUBJECT`. The options for subject refer to your request filter. These are the conditions that OpenID Connect uses to determine when to issue an authentication token.
|
"clientId": "<GUID>",
|
||||||
* specific environment
|
"clientSecret": "<CLIENT_SECRET_VALUE>",
|
||||||
```azurecli
|
"subscriptionId": "<GUID>",
|
||||||
az rest --method POST --uri 'https://graph.microsoft.com/beta/applications/<APPLICATION-OBJECT-ID>/federatedIdentityCredentials' --body '{"name":"<CREDENTIAL-NAME>","issuer":"https://token.actions.githubusercontent.com","subject":"repo:octo-org/octo-repo:environment:Production","description":"Testing","audiences":["api://AzureADTokenExchange"]}'
|
"tenantId": "<GUID>",
|
||||||
```
|
(...)
|
||||||
* pull_request events
|
}
|
||||||
```azurecli
|
|
||||||
az rest --method POST --uri 'https://graph.microsoft.com/beta/applications/<APPLICATION-OBJECT-ID>/federatedIdentityCredentials' --body '{"name":"<CREDENTIAL-NAME>","issuer":"https://token.actions.githubusercontent.com","subject":"repo:octo-org/octo-repo:pull_request","description":"Testing","audiences":["api://AzureADTokenExchange"]}'
|
to prevent unnecessary masking of `{ } ` in your logs which are in dictionary form.
|
||||||
```
|
|
||||||
* specific branch
|
5. Paste the entire JSON object produced by the `az ad sp create-for-rbac` command as the secret value and save the secret.
|
||||||
```azurecli
|
|
||||||
az rest --method POST --uri 'https://graph.microsoft.com/beta/applications/<APPLICATION-OBJECT-ID>/federatedIdentityCredentials' --body '{"name":"<CREDENTIAL-NAME>","issuer":"https://token.actions.githubusercontent.com","subject":"repo:octo-org/octo-repo:ref:refs/heads/{Branch}","description":"Testing","audiences":["api://AzureADTokenExchange"]}'
|
NOTE: to manage service principals created with `az ad sp create-for-rbac`, visit the [Azure portal](https://portal.azure.com), navigate to your Azure Active Directory, then select **Manage** > **App registrations** on the left-hand menu. Your service principal should appear in the list. Select a principal to navigate to its properties. You can also manage role assignments using the [az role assignment](https://docs.microsoft.com/cli/azure/role/assignment?view=azure-cli-latest) command.
|
||||||
```
|
|
||||||
* specific tag
|
NOTE: Currently there is no support for passing in the Subscription ID, Tenant ID, Client ID, and Client Secret as individual parameters instead of bundled in a single JSON object (creds).
|
||||||
```azurecli
|
However, a simple workaround for users who need this option can be:
|
||||||
az rest --method POST --uri 'https://graph.microsoft.com/beta/applications/<APPLICATION-OBJECT-ID>/federatedIdentityCredentials' --body '{"name":"<CREDENTIAL-NAME>","issuer":"https://token.actions.githubusercontent.com","subject":"repo:octo-org/octo-repo:ref:refs/heads/{Tag}","description":"Testing","audiences":["api://AzureADTokenExchange"]}'
|
```yaml
|
||||||
```
|
- uses: Azure/login@v1.1
|
||||||
|
with:
|
||||||
## Support for using `allow-no-subscriptions` flag with az login
|
creds: '{"clientId":"${{ secrets.CLIENT_ID }}","clientSecret":"${{ secrets.CLIENT_SECRET }}","subscriptionId":"${{ secrets.SUBSCRIPTION_ID }}","tenantId":"${{ secrets.TENANT_ID }}"}'
|
||||||
|
```
|
||||||
Capability has been added to support access to tenants without subscriptions for both OIDC and non-OIDC. This can be useful to run tenant level commands, such as `az ad`. The action accepts an optional parameter `allow-no-subscriptions` which is `false` by default.
|
In a similar way, any additional parameter can be addded to creds such as resourceManagerEndpointUrl for Azure Stack, for example.
|
||||||
|
|
||||||
```yaml
|
NOTE: If you want to hand craft your JSON object instead of using the output from the CLI command (for example, after using the UI to create the App Registration and Role assignment) the following fields are required:
|
||||||
# File: .github/workflows/workflow.yml
|
```json
|
||||||
|
{
|
||||||
on: [push]
|
"clientId": "<GUID>",
|
||||||
|
"tenantId": "<GUID>",
|
||||||
name: AzureLoginWithNoSubscriptions
|
"clientSecret": "<CLIENT_SECRET_VALUE>",
|
||||||
|
"subscriptionId": "<GUID>",
|
||||||
jobs:
|
"resourceManagerEndpointUrl": "<URL>}
|
||||||
|
```
|
||||||
build-and-deploy:
|
The resourceManagerEndpointUrl will be `https://management.azure.com/` if you are using the public azure cloud.
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
## Support for using `allow-no-subscriptions` flag with az login
|
||||||
|
|
||||||
- uses: azure/login@v1
|
Capability has been added to support access to tenants without subscriptions. This can be useful to run tenant level commands, such as `az ad`. The action accepts an optional parameter `allow-no-subscriptions` which is `false` by default.
|
||||||
with:
|
|
||||||
creds: ${{ secrets.AZURE_CREDENTIALS }}
|
```yaml
|
||||||
allow-no-subscriptions: true
|
# File: .github/workflows/workflow.yml
|
||||||
```
|
|
||||||
## Az logout and security hardening
|
on: [push]
|
||||||
|
|
||||||
This action doesn't implement ```az logout``` by default at the end of execution. However there is no way of tampering the credentials or account information because the github hosted runner is on a VM that will get reimaged for every customer run which gets everything deleted. But if the runner is self-hosted which is not github provided it is recommended to manually logout at the end of the workflow as shown below. More details on security of the runners can be found [here](https://docs.github.com/en/actions/learn-github-actions/security-hardening-for-github-actions#hardening-for-self-hosted-runners).
|
name: AzureLoginWithNoSubscriptions
|
||||||
```
|
|
||||||
- name: Azure CLI script
|
jobs:
|
||||||
uses: azure/CLI@v1
|
|
||||||
with:
|
build-and-deploy:
|
||||||
inlineScript: |
|
runs-on: ubuntu-latest
|
||||||
az logout
|
steps:
|
||||||
az cache purge
|
|
||||||
az account clear
|
- uses: azure/login@v1
|
||||||
```
|
with:
|
||||||
|
creds: ${{ secrets.AZURE_CREDENTIALS }}
|
||||||
# Contributing
|
allow-no-subscriptions: true
|
||||||
|
```
|
||||||
This project welcomes contributions and suggestions. Most contributions require you to agree to a
|
## Az logout and security hardening
|
||||||
Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us
|
|
||||||
the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com.
|
This action doesn't implement ```az logout``` by default at the end of execution. However there is no way of tampering the credentials or account information because the github hosted runner is on a VM that will get reimaged for every customer run which gets everything deleted. But if the runner is self-hosted which is not github provided it is recommended to manually logout at the end of the workflow as shown below. More details on security of the runners can be found [here](https://docs.github.com/en/actions/learn-github-actions/security-hardening-for-github-actions#hardening-for-self-hosted-runners).
|
||||||
|
```
|
||||||
When you submit a pull request, a CLA bot will automatically determine whether you need to provide
|
- name: Azure CLI script
|
||||||
a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions
|
uses: azure/CLI@v1
|
||||||
provided by the bot. You will only need to do this once across all repos using our CLA.
|
with:
|
||||||
|
azcliversion: 2.0.72
|
||||||
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
|
inlineScript: |
|
||||||
For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or
|
az logout
|
||||||
contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
|
az cache purge
|
||||||
|
az account clear
|
||||||
|
```
|
||||||
|
# Contributing
|
||||||
|
|
||||||
|
This project welcomes contributions and suggestions. Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com.
|
||||||
|
|
||||||
|
For detailed developer guidelines, visit [developer guidelines for azure actions](https://github.com/Azure/actions/blob/main/docs/developer-guildelines.md).
|
||||||
|
|
||||||
|
When you submit a pull request, a CLA bot will automatically determine whether you need to provide a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions provided by the bot. You will only need to do this once across all repos using our CLA.
|
||||||
|
|
||||||
|
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ jest.mock('../../src/PowerShell/Utilities/PowerShellToolRunner');
|
|||||||
let spnlogin: ServicePrincipalLogin;
|
let spnlogin: ServicePrincipalLogin;
|
||||||
|
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
spnlogin = new ServicePrincipalLogin("servicePrincipalID", "servicePrinicipalkey", null, "tenantId", "subscriptionId", false, null, null);
|
spnlogin = new ServicePrincipalLogin("servicePrincipalID", "servicePrinicipalkey", "tenantId", "subscriptionId", false, null, null);
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
|
|||||||
12
action.yml
12
action.yml
@@ -6,13 +6,13 @@ inputs:
|
|||||||
description: 'Paste output of `az ad sp create-for-rbac` as value of secret variable: AZURE_CREDENTIALS'
|
description: 'Paste output of `az ad sp create-for-rbac` as value of secret variable: AZURE_CREDENTIALS'
|
||||||
required: false
|
required: false
|
||||||
client-id:
|
client-id:
|
||||||
description: 'ClientId of the Azure Service principal created.'
|
description: 'ClientId of the Azure Service principal associated with OIDC Federated credential '
|
||||||
required: false
|
required: false
|
||||||
tenant-id:
|
tenant-id:
|
||||||
description: 'TenantId of the Azure Service principal created.'
|
description: 'TenantId of the Azure Service principal associated with OIDC Federated credential.'
|
||||||
required: false
|
required: false
|
||||||
subscription-id:
|
subscription-id:
|
||||||
description: 'Azure subscriptionId'
|
description: 'Azure subscriptionId scoped to the service principal with associated OIDC Federated credential'
|
||||||
required: false
|
required: false
|
||||||
enable-AzPSSession:
|
enable-AzPSSession:
|
||||||
description: 'Set this value to true to enable Azure PowerShell Login in addition to Az CLI login'
|
description: 'Set this value to true to enable Azure PowerShell Login in addition to Az CLI login'
|
||||||
@@ -26,13 +26,9 @@ inputs:
|
|||||||
description: 'Set this value to true to enable support for accessing tenants without subscriptions'
|
description: 'Set this value to true to enable support for accessing tenants without subscriptions'
|
||||||
required: false
|
required: false
|
||||||
default: false
|
default: false
|
||||||
audience:
|
|
||||||
description: 'Provide audience field for access-token. Default value is api://AzureADTokenExchange'
|
|
||||||
required: false
|
|
||||||
default: 'api://AzureADTokenExchange'
|
|
||||||
branding:
|
branding:
|
||||||
icon: 'login.svg'
|
icon: 'login.svg'
|
||||||
color: 'blue'
|
color: 'blue'
|
||||||
runs:
|
runs:
|
||||||
using: 'node16'
|
using: 'node12'
|
||||||
main: 'lib/main.js'
|
main: 'lib/main.js'
|
||||||
|
|||||||
@@ -38,10 +38,9 @@ const PowerShellToolRunner_1 = __importDefault(require("./Utilities/PowerShellTo
|
|||||||
const ScriptBuilder_1 = __importDefault(require("./Utilities/ScriptBuilder"));
|
const ScriptBuilder_1 = __importDefault(require("./Utilities/ScriptBuilder"));
|
||||||
const Constants_1 = __importDefault(require("./Constants"));
|
const Constants_1 = __importDefault(require("./Constants"));
|
||||||
class ServicePrincipalLogin {
|
class ServicePrincipalLogin {
|
||||||
constructor(servicePrincipalId, servicePrincipalKey, federatedToken, tenantId, subscriptionId, allowNoSubscriptionsLogin, environment, resourceManagerEndpointUrl) {
|
constructor(servicePrincipalId, servicePrincipalKey, tenantId, subscriptionId, allowNoSubscriptionsLogin, environment, resourceManagerEndpointUrl) {
|
||||||
this.servicePrincipalId = servicePrincipalId;
|
this.servicePrincipalId = servicePrincipalId;
|
||||||
this.servicePrincipalKey = servicePrincipalKey;
|
this.servicePrincipalKey = servicePrincipalKey;
|
||||||
this.federatedToken = federatedToken;
|
|
||||||
this.tenantId = tenantId;
|
this.tenantId = tenantId;
|
||||||
this.subscriptionId = subscriptionId;
|
this.subscriptionId = subscriptionId;
|
||||||
this.environment = environment;
|
this.environment = environment;
|
||||||
@@ -59,25 +58,16 @@ class ServicePrincipalLogin {
|
|||||||
login() {
|
login() {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
let output = "";
|
let output = "";
|
||||||
let commandStdErr = false;
|
|
||||||
const options = {
|
const options = {
|
||||||
listeners: {
|
listeners: {
|
||||||
stdout: (data) => {
|
stdout: (data) => {
|
||||||
output += data.toString();
|
output += data.toString();
|
||||||
},
|
|
||||||
stderr: (data) => {
|
|
||||||
let error = data.toString();
|
|
||||||
if (error && error.trim().length !== 0) {
|
|
||||||
commandStdErr = true;
|
|
||||||
core.error(error);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const args = {
|
const args = {
|
||||||
servicePrincipalId: this.servicePrincipalId,
|
servicePrincipalId: this.servicePrincipalId,
|
||||||
servicePrincipalKey: this.servicePrincipalKey,
|
servicePrincipalKey: this.servicePrincipalKey,
|
||||||
federatedToken: this.federatedToken,
|
|
||||||
subscriptionId: this.subscriptionId,
|
subscriptionId: this.subscriptionId,
|
||||||
environment: this.environment,
|
environment: this.environment,
|
||||||
scopeLevel: ServicePrincipalLogin.scopeLevel,
|
scopeLevel: ServicePrincipalLogin.scopeLevel,
|
||||||
|
|||||||
@@ -40,7 +40,6 @@ class PowerShellToolRunner {
|
|||||||
}
|
}
|
||||||
static executePowerShellScriptBlock(scriptBlock, options = {}) {
|
static executePowerShellScriptBlock(scriptBlock, options = {}) {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
//Options for error handling
|
|
||||||
yield exec.exec(`"${PowerShellToolRunner.psPath}" -Command`, [scriptBlock], options);
|
yield exec.exec(`"${PowerShellToolRunner.psPath}" -Command`, [scriptBlock], options);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,17 +35,9 @@ class ScriptBuilder {
|
|||||||
if (args.environment.toLowerCase() == "azurestack") {
|
if (args.environment.toLowerCase() == "azurestack") {
|
||||||
command += `Add-AzEnvironment -Name ${args.environment} -ARMEndpoint ${args.resourceManagerEndpointUrl} | out-null;`;
|
command += `Add-AzEnvironment -Name ${args.environment} -ARMEndpoint ${args.resourceManagerEndpointUrl} | out-null;`;
|
||||||
}
|
}
|
||||||
// Separate command script for OIDC and non-OIDC
|
command += `Connect-AzAccount -ServicePrincipal -Tenant '${tenantId}' -Credential \
|
||||||
if (!!args.federatedToken) {
|
(New-Object System.Management.Automation.PSCredential('${args.servicePrincipalId}',(ConvertTo-SecureString '${args.servicePrincipalKey.replace("'", "''")}' -AsPlainText -Force))) \
|
||||||
command += `Connect-AzAccount -ServicePrincipal -ApplicationId '${args.servicePrincipalId}' -Tenant '${tenantId}' -FederatedToken '${args.federatedToken}' \
|
-Environment '${args.environment}' | out-null;`;
|
||||||
-Environment '${args.environment}' | out-null;`;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
command += `Connect-AzAccount -ServicePrincipal -Tenant '${tenantId}' -Credential \
|
|
||||||
(New-Object System.Management.Automation.PSCredential('${args.servicePrincipalId}',(ConvertTo-SecureString '${args.servicePrincipalKey.replace("'", "''")}' -AsPlainText -Force))) \
|
|
||||||
-Environment '${args.environment}' | out-null;`;
|
|
||||||
}
|
|
||||||
// command to set the subscription
|
|
||||||
if (args.scopeLevel === Constants_1.default.Subscription && !args.allowNoSubscriptionsLogin) {
|
if (args.scopeLevel === Constants_1.default.Subscription && !args.allowNoSubscriptionsLogin) {
|
||||||
command += `Set-AzContext -SubscriptionId '${args.subscriptionId}' -TenantId '${tenantId}' | out-null;`;
|
command += `Set-AzContext -SubscriptionId '${args.subscriptionId}' -TenantId '${tenantId}' | out-null;`;
|
||||||
}
|
}
|
||||||
|
|||||||
74
lib/main.js
74
lib/main.js
@@ -39,25 +39,6 @@ var azPSHostEnv = !!process.env.AZUREPS_HOST_ENVIRONMENT ? `${process.env.AZUREP
|
|||||||
function main() {
|
function main() {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
try {
|
try {
|
||||||
//Options for error handling
|
|
||||||
const loginOptions = {
|
|
||||||
silent: true,
|
|
||||||
listeners: {
|
|
||||||
stderr: (data) => {
|
|
||||||
let error = data.toString();
|
|
||||||
let startsWithWarning = error.toLowerCase().startsWith('warning');
|
|
||||||
let startsWithError = error.toLowerCase().startsWith('error');
|
|
||||||
// printing ERROR
|
|
||||||
if (error && error.trim().length !== 0 && !startsWithWarning) {
|
|
||||||
if (startsWithError) {
|
|
||||||
//removing the keyword 'ERROR' to avoid duplicates while throwing error
|
|
||||||
error = error.slice(5);
|
|
||||||
}
|
|
||||||
core.setFailed(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
// Set user agent variable
|
// Set user agent variable
|
||||||
var isAzCLISuccess = false;
|
var isAzCLISuccess = false;
|
||||||
let usrAgentRepo = `${process.env.GITHUB_REPOSITORY}`;
|
let usrAgentRepo = `${process.env.GITHUB_REPOSITORY}`;
|
||||||
@@ -92,17 +73,17 @@ function main() {
|
|||||||
const allowNoSubscriptionsLogin = core.getInput('allow-no-subscriptions').toLowerCase() === "true";
|
const allowNoSubscriptionsLogin = core.getInput('allow-no-subscriptions').toLowerCase() === "true";
|
||||||
//Check for the credentials in individual parameters in the workflow.
|
//Check for the credentials in individual parameters in the workflow.
|
||||||
var servicePrincipalId = core.getInput('client-id', { required: false });
|
var servicePrincipalId = core.getInput('client-id', { required: false });
|
||||||
|
;
|
||||||
var servicePrincipalKey = null;
|
var servicePrincipalKey = null;
|
||||||
var tenantId = core.getInput('tenant-id', { required: false });
|
var tenantId = core.getInput('tenant-id', { required: false });
|
||||||
var subscriptionId = core.getInput('subscription-id', { required: false });
|
var subscriptionId = core.getInput('subscription-id', { required: false });
|
||||||
var resourceManagerEndpointUrl = "https://management.azure.com/";
|
var resourceManagerEndpointUrl = "https://management.azure.com/";
|
||||||
var enableOIDC = true;
|
var enableOIDC = true;
|
||||||
var federatedToken = null;
|
|
||||||
// If any of the individual credentials (clent_id, tenat_id, subscription_id) is present.
|
// If any of the individual credentials (clent_id, tenat_id, subscription_id) is present.
|
||||||
if (servicePrincipalId || tenantId || subscriptionId) {
|
if (servicePrincipalId || tenantId || subscriptionId) {
|
||||||
//If few of the individual credentials (clent_id, tenat_id, subscription_id) are missing in action inputs.
|
//If few of the individual credentials (clent_id, tenat_id, subscription_id) are missing in action inputs.
|
||||||
if (!(servicePrincipalId && tenantId && (subscriptionId || allowNoSubscriptionsLogin)))
|
if (!(servicePrincipalId && tenantId && (subscriptionId || allowNoSubscriptionsLogin)))
|
||||||
throw new Error("Few credentials are missing. ClientId, tenantId are mandatory. SubscriptionId is also mandatory if allow-no-subscriptions is not set.");
|
throw new Error("Few credentials are missing.ClientId,tenantId are mandatory. SubscriptionId is also mandatory if allow-no-subscriptions is not set.");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (creds) {
|
if (creds) {
|
||||||
@@ -132,19 +113,16 @@ function main() {
|
|||||||
// OIDC specific checks
|
// OIDC specific checks
|
||||||
if (enableOIDC) {
|
if (enableOIDC) {
|
||||||
console.log('Using OIDC authentication...');
|
console.log('Using OIDC authentication...');
|
||||||
try {
|
//generating ID-token
|
||||||
//generating ID-token
|
var idToken = yield core.getIDToken('api://AzureADTokenExchange');
|
||||||
let audience = core.getInput('audience', { required: false });
|
if (!!idToken) {
|
||||||
federatedToken = yield core.getIDToken(audience);
|
if (environment != "azurecloud")
|
||||||
if (!!federatedToken) {
|
throw new Error(`Your current environment - "${environment}" is not supported for OIDC login.`);
|
||||||
if (environment != "azurecloud")
|
if (enableAzPSSession)
|
||||||
throw new Error(`Your current environment - "${environment}" is not supported for OIDC login.`);
|
throw new Error(`Powershell login is not supported with OIDC.`);
|
||||||
let [issuer, subjectClaim] = yield jwtParser(federatedToken);
|
|
||||||
console.log("Federated token details: \n issuer - " + issuer + " \n subject claim - " + subjectClaim);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (error) {
|
else {
|
||||||
core.error(`${error.message.split(':')[1]}. Please make sure to give write permissions to id-token in the workflow.`);
|
throw new Error("Could not get ID token for authentication.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Attempting Az cli login
|
// Attempting Az cli login
|
||||||
@@ -187,26 +165,24 @@ function main() {
|
|||||||
commonArgs = commonArgs.concat("--allow-no-subscriptions");
|
commonArgs = commonArgs.concat("--allow-no-subscriptions");
|
||||||
}
|
}
|
||||||
if (enableOIDC) {
|
if (enableOIDC) {
|
||||||
commonArgs = commonArgs.concat("--federated-token", federatedToken);
|
commonArgs = commonArgs.concat("--federated-token", idToken);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
console.log("Note: Azure/login action also supports OIDC login mechanism. Refer https://github.com/azure/login#configure-a-service-principal-with-a-federated-credential-to-use-oidc-based-authentication for more details.");
|
|
||||||
commonArgs = commonArgs.concat("-p", servicePrincipalKey);
|
commonArgs = commonArgs.concat("-p", servicePrincipalKey);
|
||||||
}
|
}
|
||||||
yield executeAzCliCommand(`login`, true, loginOptions, commonArgs);
|
yield executeAzCliCommand(`login`, true, {}, commonArgs);
|
||||||
if (!allowNoSubscriptionsLogin) {
|
if (!allowNoSubscriptionsLogin) {
|
||||||
var args = [
|
var args = [
|
||||||
"--subscription",
|
"--subscription",
|
||||||
subscriptionId
|
subscriptionId
|
||||||
];
|
];
|
||||||
yield executeAzCliCommand(`account set`, true, loginOptions, args);
|
yield executeAzCliCommand(`account set`, true, {}, args);
|
||||||
}
|
}
|
||||||
isAzCLISuccess = true;
|
isAzCLISuccess = true;
|
||||||
if (enableAzPSSession) {
|
if (enableAzPSSession) {
|
||||||
// Attempting Az PS login
|
// Attempting Az PS login
|
||||||
console.log(`Running Azure PS Login`);
|
console.log(`Running Azure PS Login`);
|
||||||
var spnlogin;
|
const spnlogin = new ServicePrincipalLogin_1.ServicePrincipalLogin(servicePrincipalId, servicePrincipalKey, tenantId, subscriptionId, allowNoSubscriptionsLogin, environment, resourceManagerEndpointUrl);
|
||||||
spnlogin = new ServicePrincipalLogin_1.ServicePrincipalLogin(servicePrincipalId, servicePrincipalKey, federatedToken, tenantId, subscriptionId, allowNoSubscriptionsLogin, environment, resourceManagerEndpointUrl);
|
|
||||||
yield spnlogin.initialize();
|
yield spnlogin.initialize();
|
||||||
yield spnlogin.login();
|
yield spnlogin.login();
|
||||||
}
|
}
|
||||||
@@ -214,11 +190,12 @@ function main() {
|
|||||||
}
|
}
|
||||||
catch (error) {
|
catch (error) {
|
||||||
if (!isAzCLISuccess) {
|
if (!isAzCLISuccess) {
|
||||||
core.setFailed("Az CLI Login failed. Please check the credentials and make sure az is installed on the runner. For more information refer https://aka.ms/create-secrets-for-GitHub-workflows");
|
core.error("Az CLI Login failed. Please check the credentials. For more information refer https://aka.ms/create-secrets-for-GitHub-workflows");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
core.setFailed(`Azure PowerShell Login failed. Please check the credentials and make sure az is installed on the runner. For more information refer https://aka.ms/create-secrets-for-GitHub-workflows"`);
|
core.error(`Azure PowerShell Login failed. Please check the credentials. For more information refer https://aka.ms/create-secrets-for-GitHub-workflows"`);
|
||||||
}
|
}
|
||||||
|
core.setFailed(error);
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
// Reset AZURE_HTTP_USER_AGENT
|
// Reset AZURE_HTTP_USER_AGENT
|
||||||
@@ -230,15 +207,12 @@ function main() {
|
|||||||
function executeAzCliCommand(command, silent, execOptions = {}, args = []) {
|
function executeAzCliCommand(command, silent, execOptions = {}, args = []) {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
execOptions.silent = !!silent;
|
execOptions.silent = !!silent;
|
||||||
yield exec.exec(`"${azPath}" ${command}`, args, execOptions);
|
try {
|
||||||
});
|
yield exec.exec(`"${azPath}" ${command}`, args, execOptions);
|
||||||
}
|
}
|
||||||
function jwtParser(federatedToken) {
|
catch (error) {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
throw new Error(error);
|
||||||
let tokenPayload = federatedToken.split('.')[1];
|
}
|
||||||
let bufferObj = Buffer.from(tokenPayload, "base64");
|
|
||||||
let decodedPayload = JSON.parse(bufferObj.toString("utf8"));
|
|
||||||
return [decodedPayload['iss'], decodedPayload['sub']];
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
main();
|
main();
|
||||||
|
|||||||
6838
package-lock.json
generated
6838
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -15,20 +15,17 @@ export class ServicePrincipalLogin implements IAzurePowerShellSession {
|
|||||||
subscriptionId: string;
|
subscriptionId: string;
|
||||||
resourceManagerEndpointUrl: string;
|
resourceManagerEndpointUrl: string;
|
||||||
allowNoSubscriptionsLogin: boolean;
|
allowNoSubscriptionsLogin: boolean;
|
||||||
federatedToken: string;
|
|
||||||
|
|
||||||
constructor(servicePrincipalId: string,
|
constructor(servicePrincipalId: string,
|
||||||
servicePrincipalKey: string,
|
servicePrincipalKey: string,
|
||||||
federatedToken: string,
|
tenantId: string,
|
||||||
tenantId: string,
|
|
||||||
subscriptionId: string,
|
subscriptionId: string,
|
||||||
allowNoSubscriptionsLogin: boolean,
|
allowNoSubscriptionsLogin: boolean,
|
||||||
environment: string,
|
environment: string,
|
||||||
resourceManagerEndpointUrl: string) {
|
resourceManagerEndpointUrl: string) {
|
||||||
|
|
||||||
this.servicePrincipalId = servicePrincipalId;
|
this.servicePrincipalId = servicePrincipalId;
|
||||||
this.servicePrincipalKey = servicePrincipalKey;
|
this.servicePrincipalKey = servicePrincipalKey;
|
||||||
this.federatedToken = federatedToken;
|
|
||||||
this.tenantId = tenantId;
|
this.tenantId = tenantId;
|
||||||
this.subscriptionId = subscriptionId;
|
this.subscriptionId = subscriptionId;
|
||||||
this.environment = environment;
|
this.environment = environment;
|
||||||
@@ -45,26 +42,16 @@ export class ServicePrincipalLogin implements IAzurePowerShellSession {
|
|||||||
|
|
||||||
async login() {
|
async login() {
|
||||||
let output: string = "";
|
let output: string = "";
|
||||||
let commandStdErr = false;
|
|
||||||
const options: any = {
|
const options: any = {
|
||||||
listeners: {
|
listeners: {
|
||||||
stdout: (data: Buffer) => {
|
stdout: (data: Buffer) => {
|
||||||
output += data.toString();
|
output += data.toString();
|
||||||
},
|
|
||||||
stderr: (data: Buffer) => {
|
|
||||||
let error = data.toString();
|
|
||||||
if (error && error.trim().length !== 0)
|
|
||||||
{
|
|
||||||
commandStdErr = true;
|
|
||||||
core.error(error);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const args: any = {
|
const args: any = {
|
||||||
servicePrincipalId: this.servicePrincipalId,
|
servicePrincipalId: this.servicePrincipalId,
|
||||||
servicePrincipalKey: this.servicePrincipalKey,
|
servicePrincipalKey: this.servicePrincipalKey,
|
||||||
federatedToken: this.federatedToken,
|
|
||||||
subscriptionId: this.subscriptionId,
|
subscriptionId: this.subscriptionId,
|
||||||
environment: this.environment,
|
environment: this.environment,
|
||||||
scopeLevel: ServicePrincipalLogin.scopeLevel,
|
scopeLevel: ServicePrincipalLogin.scopeLevel,
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import * as exec from '@actions/exec';
|
|||||||
|
|
||||||
export default class PowerShellToolRunner {
|
export default class PowerShellToolRunner {
|
||||||
static psPath: string;
|
static psPath: string;
|
||||||
|
|
||||||
static async init() {
|
static async init() {
|
||||||
if(!PowerShellToolRunner.psPath) {
|
if(!PowerShellToolRunner.psPath) {
|
||||||
PowerShellToolRunner.psPath = await io.which("pwsh", true);
|
PowerShellToolRunner.psPath = await io.which("pwsh", true);
|
||||||
@@ -10,7 +11,6 @@ export default class PowerShellToolRunner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static async executePowerShellScriptBlock(scriptBlock: string, options: any = {}) {
|
static async executePowerShellScriptBlock(scriptBlock: string, options: any = {}) {
|
||||||
//Options for error handling
|
|
||||||
await exec.exec(`"${PowerShellToolRunner.psPath}" -Command`, [scriptBlock], options)
|
await exec.exec(`"${PowerShellToolRunner.psPath}" -Command`, [scriptBlock], options)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -8,23 +8,17 @@ export default class ScriptBuilder {
|
|||||||
getAzPSLoginScript(scheme: string, tenantId: string, args: any): string {
|
getAzPSLoginScript(scheme: string, tenantId: string, args: any): string {
|
||||||
let command = `Clear-AzContext -Scope Process;
|
let command = `Clear-AzContext -Scope Process;
|
||||||
Clear-AzContext -Scope CurrentUser -Force -ErrorAction SilentlyContinue;`;
|
Clear-AzContext -Scope CurrentUser -Force -ErrorAction SilentlyContinue;`;
|
||||||
|
|
||||||
if (scheme === Constants.ServicePrincipal) {
|
if (scheme === Constants.ServicePrincipal) {
|
||||||
|
|
||||||
if (args.environment.toLowerCase() == "azurestack") {
|
if (args.environment.toLowerCase() == "azurestack") {
|
||||||
command += `Add-AzEnvironment -Name ${args.environment} -ARMEndpoint ${args.resourceManagerEndpointUrl} | out-null;`;
|
command += `Add-AzEnvironment -Name ${args.environment} -ARMEndpoint ${args.resourceManagerEndpointUrl} | out-null;`;
|
||||||
}
|
}
|
||||||
// Separate command script for OIDC and non-OIDC
|
|
||||||
if (!!args.federatedToken) {
|
command += `Connect-AzAccount -ServicePrincipal -Tenant '${tenantId}' -Credential \
|
||||||
command += `Connect-AzAccount -ServicePrincipal -ApplicationId '${args.servicePrincipalId}' -Tenant '${tenantId}' -FederatedToken '${args.federatedToken}' \
|
(New-Object System.Management.Automation.PSCredential('${args.servicePrincipalId}',(ConvertTo-SecureString '${args.servicePrincipalKey.replace("'", "''")}' -AsPlainText -Force))) \
|
||||||
-Environment '${args.environment}' | out-null;`;
|
-Environment '${args.environment}' | out-null;`;
|
||||||
}
|
|
||||||
else {
|
|
||||||
command += `Connect-AzAccount -ServicePrincipal -Tenant '${tenantId}' -Credential \
|
|
||||||
(New-Object System.Management.Automation.PSCredential('${args.servicePrincipalId}',(ConvertTo-SecureString '${args.servicePrincipalKey.replace("'", "''")}' -AsPlainText -Force))) \
|
|
||||||
-Environment '${args.environment}' | out-null;`;
|
|
||||||
}
|
|
||||||
// command to set the subscription
|
|
||||||
if (args.scopeLevel === Constants.Subscription && !args.allowNoSubscriptionsLogin) {
|
if (args.scopeLevel === Constants.Subscription && !args.allowNoSubscriptionsLogin) {
|
||||||
command += `Set-AzContext -SubscriptionId '${args.subscriptionId}' -TenantId '${tenantId}' | out-null;`;
|
command += `Set-AzContext -SubscriptionId '${args.subscriptionId}' -TenantId '${tenantId}' | out-null;`;
|
||||||
}
|
}
|
||||||
@@ -41,7 +35,7 @@ export default class ScriptBuilder {
|
|||||||
$output['${Constants.Error}'] = $_.exception.Message
|
$output['${Constants.Error}'] = $_.exception.Message
|
||||||
}
|
}
|
||||||
return ConvertTo-Json $output`;
|
return ConvertTo-Json $output`;
|
||||||
|
|
||||||
core.debug(`Azure PowerShell Login Script: ${this.script}`);
|
core.debug(`Azure PowerShell Login Script: ${this.script}`);
|
||||||
return this.script;
|
return this.script;
|
||||||
}
|
}
|
||||||
|
|||||||
451
src/main.ts
451
src/main.ts
@@ -1,239 +1,212 @@
|
|||||||
import * as core from '@actions/core';
|
import * as core from '@actions/core';
|
||||||
import * as exec from '@actions/exec';
|
import * as exec from '@actions/exec';
|
||||||
import { ExecOptions } from '@actions/exec/lib/interfaces';
|
import * as io from '@actions/io';
|
||||||
import * as io from '@actions/io';
|
import { FormatType, SecretParser } from 'actions-secret-parser';
|
||||||
import { FormatType, SecretParser } from 'actions-secret-parser';
|
import { ServicePrincipalLogin } from './PowerShell/ServicePrincipalLogin';
|
||||||
import { ServicePrincipalLogin } from './PowerShell/ServicePrincipalLogin';
|
|
||||||
|
var azPath: string;
|
||||||
var azPath: string;
|
var prefix = !!process.env.AZURE_HTTP_USER_AGENT ? `${process.env.AZURE_HTTP_USER_AGENT}` : "";
|
||||||
var prefix = !!process.env.AZURE_HTTP_USER_AGENT ? `${process.env.AZURE_HTTP_USER_AGENT}` : "";
|
var azPSHostEnv = !!process.env.AZUREPS_HOST_ENVIRONMENT ? `${process.env.AZUREPS_HOST_ENVIRONMENT}` : "";
|
||||||
var azPSHostEnv = !!process.env.AZUREPS_HOST_ENVIRONMENT ? `${process.env.AZUREPS_HOST_ENVIRONMENT}` : "";
|
|
||||||
|
async function main() {
|
||||||
async function main() {
|
try {
|
||||||
try {
|
// Set user agent variable
|
||||||
//Options for error handling
|
var isAzCLISuccess = false;
|
||||||
const loginOptions: ExecOptions = {
|
let usrAgentRepo = `${process.env.GITHUB_REPOSITORY}`;
|
||||||
silent: true,
|
let actionName = 'AzureLogin';
|
||||||
listeners: {
|
let userAgentString = (!!prefix ? `${prefix}+` : '') + `GITHUBACTIONS/${actionName}@v1_${usrAgentRepo}`;
|
||||||
stderr: (data: Buffer) => {
|
let azurePSHostEnv = (!!azPSHostEnv ? `${azPSHostEnv}+` : '') + `GITHUBACTIONS/${actionName}@v1_${usrAgentRepo}`;
|
||||||
let error = data.toString();
|
core.exportVariable('AZURE_HTTP_USER_AGENT', userAgentString);
|
||||||
let startsWithWarning = error.toLowerCase().startsWith('warning');
|
core.exportVariable('AZUREPS_HOST_ENVIRONMENT', azurePSHostEnv);
|
||||||
let startsWithError = error.toLowerCase().startsWith('error');
|
|
||||||
// printing ERROR
|
azPath = await io.which("az", true);
|
||||||
if (error && error.trim().length !== 0 && !startsWithWarning) {
|
core.debug(`az cli version used: ${azPath}`);
|
||||||
if(startsWithError) {
|
let azureSupportedCloudName = new Set([
|
||||||
//removing the keyword 'ERROR' to avoid duplicates while throwing error
|
"azureusgovernment",
|
||||||
error = error.slice(5);
|
"azurechinacloud",
|
||||||
}
|
"azuregermancloud",
|
||||||
core.setFailed(error);
|
"azurecloud",
|
||||||
}
|
"azurestack"]);
|
||||||
}
|
|
||||||
}
|
let output: string = "";
|
||||||
}
|
const execOptions: any = {
|
||||||
// Set user agent variable
|
listeners: {
|
||||||
var isAzCLISuccess = false;
|
stdout: (data: Buffer) => {
|
||||||
let usrAgentRepo = `${process.env.GITHUB_REPOSITORY}`;
|
output += data.toString();
|
||||||
let actionName = 'AzureLogin';
|
}
|
||||||
let userAgentString = (!!prefix ? `${prefix}+` : '') + `GITHUBACTIONS/${actionName}@v1_${usrAgentRepo}`;
|
}
|
||||||
let azurePSHostEnv = (!!azPSHostEnv ? `${azPSHostEnv}+` : '') + `GITHUBACTIONS/${actionName}@v1_${usrAgentRepo}`;
|
};
|
||||||
core.exportVariable('AZURE_HTTP_USER_AGENT', userAgentString);
|
await executeAzCliCommand("--version", true, execOptions);
|
||||||
core.exportVariable('AZUREPS_HOST_ENVIRONMENT', azurePSHostEnv);
|
core.debug(`az cli version used:\n${output}`);
|
||||||
|
|
||||||
azPath = await io.which("az", true);
|
let creds = core.getInput('creds', { required: false });
|
||||||
core.debug(`az cli version used: ${azPath}`);
|
let secrets = creds ? new SecretParser(creds, FormatType.JSON) : null;
|
||||||
let azureSupportedCloudName = new Set([
|
let environment = core.getInput("environment").toLowerCase();
|
||||||
"azureusgovernment",
|
const enableAzPSSession = core.getInput('enable-AzPSSession').toLowerCase() === "true";
|
||||||
"azurechinacloud",
|
const allowNoSubscriptionsLogin = core.getInput('allow-no-subscriptions').toLowerCase() === "true";
|
||||||
"azuregermancloud",
|
|
||||||
"azurecloud",
|
//Check for the credentials in individual parameters in the workflow.
|
||||||
"azurestack"]);
|
var servicePrincipalId = core.getInput('client-id', { required: false });;
|
||||||
|
var servicePrincipalKey = null;
|
||||||
let output: string = "";
|
var tenantId = core.getInput('tenant-id', { required: false });
|
||||||
const execOptions: any = {
|
var subscriptionId = core.getInput('subscription-id', { required: false });
|
||||||
listeners: {
|
var resourceManagerEndpointUrl = "https://management.azure.com/";
|
||||||
stdout: (data: Buffer) => {
|
var enableOIDC = true;
|
||||||
output += data.toString();
|
|
||||||
}
|
// If any of the individual credentials (clent_id, tenat_id, subscription_id) is present.
|
||||||
}
|
if (servicePrincipalId || tenantId || subscriptionId) {
|
||||||
};
|
|
||||||
await executeAzCliCommand("--version", true, execOptions);
|
//If few of the individual credentials (clent_id, tenat_id, subscription_id) are missing in action inputs.
|
||||||
core.debug(`az cli version used:\n${output}`);
|
if(!(servicePrincipalId && tenantId && (subscriptionId || allowNoSubscriptionsLogin)))
|
||||||
|
throw new Error("Few credentials are missing.ClientId,tenantId are mandatory. SubscriptionId is also mandatory if allow-no-subscriptions is not set.");
|
||||||
let creds = core.getInput('creds', { required: false });
|
}
|
||||||
let secrets = creds ? new SecretParser(creds, FormatType.JSON) : null;
|
else{
|
||||||
let environment = core.getInput("environment").toLowerCase();
|
if (creds) {
|
||||||
const enableAzPSSession = core.getInput('enable-AzPSSession').toLowerCase() === "true";
|
core.debug('using creds JSON...');
|
||||||
const allowNoSubscriptionsLogin = core.getInput('allow-no-subscriptions').toLowerCase() === "true";
|
enableOIDC = false;
|
||||||
|
servicePrincipalId = secrets.getSecret("$.clientId", true);
|
||||||
//Check for the credentials in individual parameters in the workflow.
|
servicePrincipalKey = secrets.getSecret("$.clientSecret", true);
|
||||||
var servicePrincipalId = core.getInput('client-id', { required: false });
|
tenantId = secrets.getSecret("$.tenantId", true);
|
||||||
var servicePrincipalKey = null;
|
subscriptionId = secrets.getSecret("$.subscriptionId", true);
|
||||||
var tenantId = core.getInput('tenant-id', { required: false });
|
resourceManagerEndpointUrl = secrets.getSecret("$.resourceManagerEndpointUrl", false);
|
||||||
var subscriptionId = core.getInput('subscription-id', { required: false });
|
}
|
||||||
var resourceManagerEndpointUrl = "https://management.azure.com/";
|
else {
|
||||||
var enableOIDC = true;
|
throw new Error("Credentials are not passed for Login action.");
|
||||||
var federatedToken = null;
|
}
|
||||||
|
}
|
||||||
// If any of the individual credentials (clent_id, tenat_id, subscription_id) is present.
|
//generic checks
|
||||||
if (servicePrincipalId || tenantId || subscriptionId) {
|
//servicePrincipalKey is only required in non-oidc scenario.
|
||||||
|
if (!servicePrincipalId || !tenantId || !(servicePrincipalKey || enableOIDC)) {
|
||||||
//If few of the individual credentials (clent_id, tenat_id, subscription_id) are missing in action inputs.
|
throw new Error("Not all values are present in the credentials. Ensure clientId, clientSecret and tenantId are supplied.");
|
||||||
if (!(servicePrincipalId && tenantId && (subscriptionId || allowNoSubscriptionsLogin)))
|
}
|
||||||
throw new Error("Few credentials are missing. ClientId, tenantId are mandatory. SubscriptionId is also mandatory if allow-no-subscriptions is not set.");
|
if (!subscriptionId && !allowNoSubscriptionsLogin) {
|
||||||
}
|
throw new Error("Not all values are present in the credentials. Ensure subscriptionId is supplied.");
|
||||||
else {
|
}
|
||||||
if (creds) {
|
if (!azureSupportedCloudName.has(environment)) {
|
||||||
core.debug('using creds JSON...');
|
throw new Error("Unsupported value for environment is passed.The list of supported values for environment are ‘azureusgovernment', ‘azurechinacloud’, ‘azuregermancloud’, ‘azurecloud’ or ’azurestack’");
|
||||||
enableOIDC = false;
|
}
|
||||||
servicePrincipalId = secrets.getSecret("$.clientId", true);
|
|
||||||
servicePrincipalKey = secrets.getSecret("$.clientSecret", true);
|
// OIDC specific checks
|
||||||
tenantId = secrets.getSecret("$.tenantId", true);
|
if (enableOIDC) {
|
||||||
subscriptionId = secrets.getSecret("$.subscriptionId", true);
|
console.log('Using OIDC authentication...')
|
||||||
resourceManagerEndpointUrl = secrets.getSecret("$.resourceManagerEndpointUrl", false);
|
//generating ID-token
|
||||||
}
|
var idToken = await core.getIDToken('api://AzureADTokenExchange');
|
||||||
else {
|
if (!!idToken) {
|
||||||
throw new Error("Credentials are not passed for Login action.");
|
if (environment != "azurecloud")
|
||||||
}
|
throw new Error(`Your current environment - "${environment}" is not supported for OIDC login.`);
|
||||||
}
|
if (enableAzPSSession)
|
||||||
//generic checks
|
throw new Error(`Powershell login is not supported with OIDC.`);
|
||||||
//servicePrincipalKey is only required in non-oidc scenario.
|
}
|
||||||
if (!servicePrincipalId || !tenantId || !(servicePrincipalKey || enableOIDC)) {
|
else {
|
||||||
throw new Error("Not all values are present in the credentials. Ensure clientId, clientSecret and tenantId are supplied.");
|
throw new Error("Could not get ID token for authentication.");
|
||||||
}
|
}
|
||||||
if (!subscriptionId && !allowNoSubscriptionsLogin) {
|
}
|
||||||
throw new Error("Not all values are present in the credentials. Ensure subscriptionId is supplied.");
|
|
||||||
}
|
// Attempting Az cli login
|
||||||
if (!azureSupportedCloudName.has(environment)) {
|
if (environment == "azurestack") {
|
||||||
throw new Error("Unsupported value for environment is passed.The list of supported values for environment are ‘azureusgovernment', ‘azurechinacloud’, ‘azuregermancloud’, ‘azurecloud’ or ’azurestack’");
|
if (!resourceManagerEndpointUrl) {
|
||||||
}
|
throw new Error("resourceManagerEndpointUrl is a required parameter when environment is defined.");
|
||||||
|
}
|
||||||
// OIDC specific checks
|
|
||||||
if (enableOIDC) {
|
console.log(`Unregistering cloud: "${environment}" first if it exists`);
|
||||||
console.log('Using OIDC authentication...')
|
try {
|
||||||
try {
|
await executeAzCliCommand(`cloud set -n AzureCloud`, true);
|
||||||
//generating ID-token
|
await executeAzCliCommand(`cloud unregister -n "${environment}"`, false);
|
||||||
let audience = core.getInput('audience', { required: false });
|
}
|
||||||
federatedToken = await core.getIDToken(audience);
|
catch (error) {
|
||||||
if (!!federatedToken) {
|
console.log(`Ignore cloud not registered error: "${error}"`);
|
||||||
if (environment != "azurecloud")
|
}
|
||||||
throw new Error(`Your current environment - "${environment}" is not supported for OIDC login.`);
|
|
||||||
let [issuer, subjectClaim] = await jwtParser(federatedToken);
|
console.log(`Registering cloud: "${environment}" with ARM endpoint: "${resourceManagerEndpointUrl}"`);
|
||||||
console.log("Federated token details: \n issuer - " + issuer + " \n subject claim - " + subjectClaim);
|
try {
|
||||||
}
|
let baseUri = resourceManagerEndpointUrl;
|
||||||
}
|
if (baseUri.endsWith('/')) {
|
||||||
catch (error) {
|
baseUri = baseUri.substring(0, baseUri.length - 1); // need to remove trailing / from resourceManagerEndpointUrl to correctly derive suffixes below
|
||||||
core.error(`${error.message.split(':')[1]}. Please make sure to give write permissions to id-token in the workflow.`);
|
}
|
||||||
}
|
let suffixKeyvault = ".vault" + baseUri.substring(baseUri.indexOf('.')); // keyvault suffix starts with .
|
||||||
}
|
let suffixStorage = baseUri.substring(baseUri.indexOf('.') + 1); // storage suffix starts without .
|
||||||
|
let profileVersion = "2019-03-01-hybrid";
|
||||||
// Attempting Az cli login
|
await executeAzCliCommand(`cloud register -n "${environment}" --endpoint-resource-manager "${resourceManagerEndpointUrl}" --suffix-keyvault-dns "${suffixKeyvault}" --suffix-storage-endpoint "${suffixStorage}" --profile "${profileVersion}"`, false);
|
||||||
if (environment == "azurestack") {
|
}
|
||||||
if (!resourceManagerEndpointUrl) {
|
catch (error) {
|
||||||
throw new Error("resourceManagerEndpointUrl is a required parameter when environment is defined.");
|
core.error(`Error while trying to register cloud "${environment}": "${error}"`);
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`Unregistering cloud: "${environment}" first if it exists`);
|
console.log(`Done registering cloud: "${environment}"`)
|
||||||
try {
|
}
|
||||||
await executeAzCliCommand(`cloud set -n AzureCloud`, true);
|
|
||||||
await executeAzCliCommand(`cloud unregister -n "${environment}"`, false);
|
await executeAzCliCommand(`cloud set -n "${environment}"`, false);
|
||||||
}
|
console.log(`Done setting cloud: "${environment}"`);
|
||||||
catch (error) {
|
|
||||||
console.log(`Ignore cloud not registered error: "${error}"`);
|
// Attempting Az cli login
|
||||||
}
|
var commonArgs = ["--service-principal",
|
||||||
|
"-u", servicePrincipalId,
|
||||||
console.log(`Registering cloud: "${environment}" with ARM endpoint: "${resourceManagerEndpointUrl}"`);
|
"--tenant", tenantId
|
||||||
try {
|
];
|
||||||
let baseUri = resourceManagerEndpointUrl;
|
if (allowNoSubscriptionsLogin) {
|
||||||
if (baseUri.endsWith('/')) {
|
commonArgs = commonArgs.concat("--allow-no-subscriptions");
|
||||||
baseUri = baseUri.substring(0, baseUri.length - 1); // need to remove trailing / from resourceManagerEndpointUrl to correctly derive suffixes below
|
}
|
||||||
}
|
if (enableOIDC) {
|
||||||
let suffixKeyvault = ".vault" + baseUri.substring(baseUri.indexOf('.')); // keyvault suffix starts with .
|
commonArgs = commonArgs.concat("--federated-token", idToken);
|
||||||
let suffixStorage = baseUri.substring(baseUri.indexOf('.') + 1); // storage suffix starts without .
|
}
|
||||||
let profileVersion = "2019-03-01-hybrid";
|
else {
|
||||||
await executeAzCliCommand(`cloud register -n "${environment}" --endpoint-resource-manager "${resourceManagerEndpointUrl}" --suffix-keyvault-dns "${suffixKeyvault}" --suffix-storage-endpoint "${suffixStorage}" --profile "${profileVersion}"`, false);
|
commonArgs = commonArgs.concat("-p", servicePrincipalKey);
|
||||||
}
|
}
|
||||||
catch (error) {
|
await executeAzCliCommand(`login`, true, {}, commonArgs);
|
||||||
core.error(`Error while trying to register cloud "${environment}": "${error}"`);
|
|
||||||
}
|
if(!allowNoSubscriptionsLogin){
|
||||||
|
var args = [
|
||||||
console.log(`Done registering cloud: "${environment}"`)
|
"--subscription",
|
||||||
}
|
subscriptionId
|
||||||
|
];
|
||||||
await executeAzCliCommand(`cloud set -n "${environment}"`, false);
|
await executeAzCliCommand(`account set`, true, {}, args);
|
||||||
console.log(`Done setting cloud: "${environment}"`);
|
}
|
||||||
|
isAzCLISuccess = true;
|
||||||
// Attempting Az cli login
|
if (enableAzPSSession) {
|
||||||
var commonArgs = ["--service-principal",
|
// Attempting Az PS login
|
||||||
"-u", servicePrincipalId,
|
console.log(`Running Azure PS Login`);
|
||||||
"--tenant", tenantId
|
const spnlogin: ServicePrincipalLogin = new ServicePrincipalLogin(
|
||||||
];
|
servicePrincipalId,
|
||||||
if (allowNoSubscriptionsLogin) {
|
servicePrincipalKey,
|
||||||
commonArgs = commonArgs.concat("--allow-no-subscriptions");
|
tenantId,
|
||||||
}
|
subscriptionId,
|
||||||
if (enableOIDC) {
|
allowNoSubscriptionsLogin,
|
||||||
commonArgs = commonArgs.concat("--federated-token", federatedToken);
|
environment,
|
||||||
}
|
resourceManagerEndpointUrl);
|
||||||
else {
|
await spnlogin.initialize();
|
||||||
console.log("Note: Azure/login action also supports OIDC login mechanism. Refer https://github.com/azure/login#configure-a-service-principal-with-a-federated-credential-to-use-oidc-based-authentication for more details.")
|
await spnlogin.login();
|
||||||
commonArgs = commonArgs.concat("-p", servicePrincipalKey);
|
}
|
||||||
}
|
|
||||||
await executeAzCliCommand(`login`, true, loginOptions, commonArgs);
|
console.log("Login successful.");
|
||||||
|
}
|
||||||
if (!allowNoSubscriptionsLogin) {
|
catch (error) {
|
||||||
var args = [
|
if (!isAzCLISuccess) {
|
||||||
"--subscription",
|
core.error("Az CLI Login failed. Please check the credentials. For more information refer https://aka.ms/create-secrets-for-GitHub-workflows");
|
||||||
subscriptionId
|
}
|
||||||
];
|
else {
|
||||||
await executeAzCliCommand(`account set`, true, loginOptions, args);
|
core.error(`Azure PowerShell Login failed. Please check the credentials. For more information refer https://aka.ms/create-secrets-for-GitHub-workflows"`);
|
||||||
}
|
}
|
||||||
isAzCLISuccess = true;
|
core.setFailed(error);
|
||||||
if (enableAzPSSession) {
|
}
|
||||||
// Attempting Az PS login
|
finally {
|
||||||
console.log(`Running Azure PS Login`);
|
// Reset AZURE_HTTP_USER_AGENT
|
||||||
var spnlogin: ServicePrincipalLogin;
|
core.exportVariable('AZURE_HTTP_USER_AGENT', prefix);
|
||||||
|
core.exportVariable('AZUREPS_HOST_ENVIRONMENT', azPSHostEnv);
|
||||||
spnlogin = new ServicePrincipalLogin(
|
}
|
||||||
servicePrincipalId,
|
}
|
||||||
servicePrincipalKey,
|
|
||||||
federatedToken,
|
async function executeAzCliCommand(
|
||||||
tenantId,
|
command: string,
|
||||||
subscriptionId,
|
silent?: boolean,
|
||||||
allowNoSubscriptionsLogin,
|
execOptions: any = {},
|
||||||
environment,
|
args: any = []) {
|
||||||
resourceManagerEndpointUrl);
|
execOptions.silent = !!silent;
|
||||||
await spnlogin.initialize();
|
try {
|
||||||
await spnlogin.login();
|
await exec.exec(`"${azPath}" ${command}`, args, execOptions);
|
||||||
}
|
}
|
||||||
|
catch (error) {
|
||||||
console.log("Login successful.");
|
throw new Error(error);
|
||||||
}
|
}
|
||||||
catch (error) {
|
}
|
||||||
if (!isAzCLISuccess) {
|
|
||||||
core.setFailed("Az CLI Login failed. Please check the credentials and make sure az is installed on the runner. For more information refer https://aka.ms/create-secrets-for-GitHub-workflows");
|
main();
|
||||||
}
|
|
||||||
else {
|
|
||||||
core.setFailed(`Azure PowerShell Login failed. Please check the credentials and make sure az is installed on the runner. For more information refer https://aka.ms/create-secrets-for-GitHub-workflows"`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
// Reset AZURE_HTTP_USER_AGENT
|
|
||||||
core.exportVariable('AZURE_HTTP_USER_AGENT', prefix);
|
|
||||||
core.exportVariable('AZUREPS_HOST_ENVIRONMENT', azPSHostEnv);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function executeAzCliCommand(
|
|
||||||
command: string,
|
|
||||||
silent?: boolean,
|
|
||||||
execOptions: any = {},
|
|
||||||
args: any = []) {
|
|
||||||
execOptions.silent = !!silent;
|
|
||||||
await exec.exec(`"${azPath}" ${command}`, args, execOptions);
|
|
||||||
}
|
|
||||||
async function jwtParser(federatedToken: string) {
|
|
||||||
let tokenPayload = federatedToken.split('.')[1];
|
|
||||||
let bufferObj = Buffer.from(tokenPayload, "base64");
|
|
||||||
let decodedPayload = JSON.parse(bufferObj.toString("utf8"));
|
|
||||||
return [decodedPayload['iss'], decodedPayload['sub']];
|
|
||||||
}
|
|
||||||
main();
|
|
||||||
|
|||||||
Reference in New Issue
Block a user