Git Product home page Git Product logo

manage-azure-policy's Introduction

Manage Azure Policy Action

With Manage Azure Policy Action you can now create or update Azure policies from your GitHub Workflows. Since workflows are totally customizable, you can have a complete control over the sequence in which Azure policies are rolled out. Its now even easier to follow safe deployment practices and catch regressions or bugs well before policies are applied on critical resources.

New to Azure Policy? Its an Azure service that lets you enforce organizational standards and asses compliance at scale. To know more check out: Azure Policies - Overview

The definition of this Github Action is in action.yml

Pre-requisites:

  • Azure Login Action: Authenticate using Azure Login action. The Manage Azure Policy action assumes that Azure Login is done using an Azure service principal that has sufficient permissions to write policy on selected scopes. Once login is done, the next set of actions in the workflow can perform tasks such as creating policies or updating them. For more details on permissions, checkout 'Configure credentials for Azure login action' section in this page or alternatively you can refer the full documentation of Azure Login Action.
  • Azure Checkout Action: All policies files should be downloaded from the GitHub repository to the GitHub runner. You can use checkout action for doing so. Refer the 'End-to-End Sample Workflows' section in this page for examples.
  • Azure Policy files should be present in the following directory structure. You can also export policies from Azure portal. (Go to Definitions section in Azure Policy and Click on Export definitions button)
.
|
|- policies/  ____________________________ # Root folder for policies
|  |- <policy1_name>/  ___________________ # Subfolder for a policy
|     |- policy.json _____________________ # Policy definition
|     |- assign.<name1>.json _____________ # Assignment1 for the policy definition in this folder
|     |- assign.<name2>.json _____________ # Assignment2 for the policy definition in this folder
|     |- assign.<name3>.json _____________ # Assignment3 for the policy definition in this folder
|
|  |- <policy2_name>/  ___________________ # Subfolder for another policy
|     |- policy.json _____________________ # Policy definition
|     |- assign.<name1>.json _____________ # Assignment1 for the policy definition in this folder
|     |- assign.<name2>.json _____________ # Assignment2 for the policy definition in this folder
|     |- assign.<name3>.json _____________ # Assignment3 for the policy definition in this folder
|     |- assign.<name4>.json _____________ # Assignment4 for the policy definition in this folder
|     |- assign.<name5>.json _____________ # Assignment5 for the policy definition in this folder

Inputs for the Action

  • paths: mandatory. The path(s) to the directory that contains Azure policy files. The files present only in these directories will be considered by this action for updating policies in Azure. You can use wild card characters as mentioned * or ** for specifying sub folders in a path. For more details on the use of the wild cards check glob wildcard patterns. Note that a definition file should be named as 'policy.json' and assignment filenames should start with 'assign' keyword.
  • ignore-paths: Optional. These are the directory paths that will be ignored by the action. If you have a specific policy folder that is not ready to be applied yet, specify the path here. Note that ignore-paths has a higher precedence compared to paths parameter.
  • assignments: Optional. These are policy assignment files that would be considered by the action. This parameter is especially useful if you want to apply only those assignments that correspond to a specific environment for following a safe deployment practice. E.g. assign.AllowedVMSKUs-dev-rg.json. You can use wild card character '*' to match multiple file names. E.g. assign.*dev*.json. If this parameter is not specified, the action will consider all assignment files that are present in the directories mentioned in paths parameter.
  • mode: Optional. There are 2 modes for this action - incremental and complete. If not specified, the action will use incremental mode by default. In incremental mode, the action will compare already exisiting policy in azure with the contents of policy provided in repository file. It will apply the policy only if there is a mismatch. On the contrary, the complete mode will apply all the files present in the specified paths irrespective of whether or not repository policy file has been updated.

End-to-End Sample Workflows

Sample workflow to apply all policy file changes in a given directory to Azure Policy

# File: .github/workflows/workflow.yml

on: push

jobs:
  apply-azure-policy:    
    runs-on: ubuntu-latest
    steps:
    # Azure Login       
    - name: Login to Azure
      uses: azure/login@v1
      with:
        creds: ${{secrets.AZURE_CREDENTIALS}}

    - name: Checkout
      uses: actions/checkout@v2 

    - name: Create or Update Azure Policies
      uses: azure/manage-azure-policy@v0
      with:      
        paths:  |                  
          policies/**
        

The above workflow will apply policy files changees in policies/** (see pattern syntax) directory to Azure Policy.

Sample workflow to apply only a subset of assignments from a given directory to Azure Policy

# File: .github/workflows/workflow.yml

on: push

jobs:
  apply-azure-policy:    
    runs-on: ubuntu-latest
    steps:
    # Azure Login
    - name: Login to Azure
      uses: azure/login@v1
      with:
        creds: ${{secrets.AZURE_CREDENTIALS}}

    - name: Checkout
      uses: actions/checkout@v2 

    - name: Create or Update Azure Policies
      uses: azure/manage-azure-policy@v0
      with:      
        paths:  |                
          policies/**
        assignments:  |
          assign.*_testRG_*.json
        

The above workflow will apply policy files changes only in policies/** directory. For each directory, the action will first apply the definition and then assignments that have 'testRG' in their filename. This assignment field is especially useful for risk mitigation scenarios, where you first want to apply assignments corresponding to a specific environment like 'test'.

Sample workflow to apply policies at Management Group scope

# File: .github/workflows/workflow.yml

on: push

jobs:
  apply-azure-policy:    
    runs-on: ubuntu-latest
    steps:
    # Azure Login at management group scope requires allow-no-subscriptions to be set to true
    - name: Login to Azure
      uses: azure/login@v1
      with:
        creds: ${{secrets.AZURE_CREDENTIALS}}
        allow-no-subscriptions: true

    - name: Checkout
      uses: actions/checkout@v2 

    - name: Create or Update Azure Policies
      uses: azure/manage-azure-policy@v0
      with:      
        paths:  |                
          policies/**
        

For deploying policies or initiatives at a management group level, the azure login action should have input allow-no-subscriptions set to true.

Manage Azure Policy Action is supported for the Azure public cloud as well as Azure government clouds ('AzureUSGovernment' or 'AzureChinaCloud') and Azure Stack ('AzureStack') Hub. Before running this action, login to the respective Azure Cloud using Azure Login by setting appropriate value for the environment parameter.

Quickstart Video Tutorials:

  1. Export Azure Policy resources to GitHub Repository
  2. Deploy Azure Policies with GitHub workflows

Configure credentials for Azure login action:

With the Azure login Action, you can perform an Azure login using Azure service principal. The credentials of Azure Service Principal can be added as secrets in the GitHub repository and then used in the workflow. Follow the below steps to generate credentials and store in github.

  • Prerequisite: You should have installed Azure cli on your local machine to run the command or use the cloudshell in the Azure portal. To install Azure cli, follow Install Azure Cli. To use cloudshell, follow CloudShell Quickstart. After you have one of the above ready, follow these steps:

  • To create service principal that has access over subscription scope, run the below Azure CLI command and copy the output JSON object to your clipboard.

  
   az ad sp create-for-rbac --name "myApp" --role "Resource Policy Contributor"  \
                            --scopes /subscriptions/{subscription-id} \
                            --sdk-auth
                            
  # Replace {subscription-id} with the subscription identifiers
  
  # The command should output a JSON object similar to this:

  {
    "clientId": "<GUID>",
    "clientSecret": "<GUID>",
    "subscriptionId": "<GUID>",
    "tenantId": "<GUID>",
    (...)
  }
  
  • Alternatively, to create service principal that has access over atleast one management group scope, run the below Azure CLI command.
  
   az ad sp create-for-rbac --name "myApp" --role "Resource Policy Contributor"  \
                            --scopes  /providers/Microsoft.Management/managementGroups/{management-group-id} \

                            
  # Replace {management-group-name} with the management group identifier
  
  # The command should output a JSON object similar to this:

  {
    "appId": "<GUID>",
    "displayName": "<display-name>",
    "name": "<url>",
    "password": "<GUID>",
    "tenant": "<GUID>"
  }
  
  # copy the GUID values for appId, password and tenant from above JSON and replace them in the following JSON. Once replaced, copy the JSON to clipboard
   
  {
    "clientId": "<appId>",
    "clientSecret": "<password>",  
    "tenantId": "<tenant>"
  }
  
  
  
  • Define a 'New secret' under your GitHub repository settings -> 'Secrets' menu. Lets name it 'AZURE_CREDENTIALS'.
  • Paste the contents of the clipboard as the value of the above secret variable.
  • Use the secret variable in the Azure Login Action(Refer the End-to-End Sample Workflows section )

If needed, you can modify the Azure CLI command to further reduce the scope for which permissions are provided. Here is the command that gives contributor access to only a resource group.

  
   az ad sp create-for-rbac --name "myApp" --role "Resource Policy Contributor"  \
                            --scopes /subscriptions/{subscription-id}/resourceGroups/{resource-group} \
                            --sdk-auth
                            
  # Replace {subscription-id}, {resource-group} with the subscription and resource group identifiers.
  

You can also provide permissions to multiple scopes using the Azure CLI command:

  
   az ad sp create-for-rbac --name "myApp" --role "Resource Policy Contributor"  \
                            --scopes /subscriptions/{subscription-id}/resourceGroups/{resource-group1} \
                            /subscriptions/{subscription-id}/resourceGroups/{resource-group2} \
                            --sdk-auth
                            
  # Replace {subscription-id}, {resource-group1}, {resource-group2} with the subscription and resource group identifiers.
  

Feedback

If you have any changes you’d like to see or suggestions for this action, we’d love your feedback ❤️ . Please feel free to raise a GitHub issue in this repository describing your suggestion. This would enable us to label and track it properly. You can do the same if you encounter a problem with the feature as well.

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.

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. For more information see the Code of Conduct FAQ or contact [email protected] with any additional questions or comments.

manage-azure-policy's People

Contributors

ajinkya599 avatar devenes avatar josh-01 avatar microsoft-github-operations[bot] avatar microsoftopensource avatar raiyanalam avatar rgsubh avatar sundargs2000 avatar tauhid621 avatar zainuvk avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

manage-azure-policy's Issues

API update to support newer properties

It would be nice if a new version is in the works with a new version of the api so that we could leverage newer properties of policy assignments such as overrides. As of now, we are unable to and the workaround is to leave them out and then manually add them afterwards. Will a lot of policies, these leads to a lot of extra work.

https://github.com/Azure/manage-azure-policy/blob/1ed950c25977c56949fee5ce2b07e6fd7aa47958/src/azure/azHttpClient.ts#L272C11-L272C21

Validate policies

Would be nice to be able to validate the policy files during CI/PR and not actually deploy them so there is more confidence that the changes will work during the release/cd.

Leverage Action for Policy Exemptions

To have the partner piece of Policy where exemptions are needed in some environments; it'd be nice to be able to manage the exemptions with code as well.

Add support for policy-as-code with policysets/initiatives

Please add support for this GHA to export policysets/initiatives and their related initiative assignments from the Azure portal.

I use over 40+ custom policies which are grouped into initiatives and assigned to the subscription scope. This allows me to quickly see/manage the compliance of initiatives at a glance without drilling down into individual policies.

Assigning policies individually to the subscription, or even resource group, becomes hard to manage at scale and across many subscriptions.

GitHub Actions gives deprecation warning

The GitHub Actions workflow gives the following deprecation warning while running the action:

Node.js 12 actions are deprecated. For more information see: github.blog/changelog/2022-09-22-github-actions-all-actions-will-begin-running-on-node16-instead-of-node12.

Not detecting policy drift changes from portal

After changing a policy (ie: updating the description) using the azure portal then running the manage policy action in incremental mode it does not detect the change and apply the policy from source.

looking at the digest of the policy in the repo and digest in the portal are not identical.

Steps
Copy policy json from portal into repo.
Modify policy description in portal.
Run action in incremental mode

Result
No changes found.

Expected
One change found and updated

Improved Warning message when folder path pattern not matching or hash value of policy is the same

Today the action shows the message "Did not find any policies to create/update. No policy files match the given patterns or no changes were detected" when:

  • No paths are found that match the given directory path pattern
  • The hash value of the file in GitHub and Azure are the same

A customer would not be able to understand which of the above is the reason for warning. This information is however present in the debug logs. In the warning message can we ask users to set 'ACTIONS_STEP_DEBUG' as true for detailed logs and make the debug logs cleaner for user to find root cause.

Feature Request - Policy Excemption

Do you have plans to add policy excemption to this very cool github action to deploy policies? We are using it at the moment, but lack the feature to excempt resources from policies when needed.

Overrides not supported due to API version

I'm getting the following error when an assignment has overrides added, can this be fixed please?

assignment. Error: The property 'overrides'
is not supported in API version '2021-06-01'.
Please use API version '2022-06-01' or higher.

Not exporting assignment created by my code

'Allowed locations', has 4 assignment. including my assignment say 'Allowed locations Mine'.
When I use 'Export Policy Definitions' and select 'Allowed location' policy it shows all 4 assignments including mine.
Now wne I go to next step and export, after exporting it notifies, 'It exported 3 assignments'.
I verified in my GitHub also, my assignment 'Allowed locations Mine' is not exported to GitHub.

Make the Sync Logic Pipeline agnostic

Currently the job is only available as Github Action. Most of the Github logic is only for debug output - as far as I've seen. It would be great if the sync logic - which is github independant - can be re-used in other Pipelines (Azure Devops, Gitlab, Jenkins, etc.) and having a Github Wrapper around that logic for this particular action.

Node.jd 16 is deprecated

Node.js was EOL-ed in september 2023.

GitHub Actions is complaining too:

Node.js 16 actions are deprecated. Please update the following actions to use Node.js 20: azure/login@v1, actions/checkout@v3, azure/manage-azure-policy@v0. For more information see: https://github.blog/changelog/2023-09-22-github-actions-transitioning-from-node-16-to-node-20/.

^ GitHub blogpost says:

Node 16 has reached its end of life, prompting us to initiate its deprecation process for GitHub Actions. Our plan is to transition all actions to run on Node 20 by Spring 2024. We will actively monitor the migration's progress and gather community feedback before finalizing the transition date. Starting October 23rd, workflows containing actions running on Node 16 will display a warning to alert users about the upcoming migration.

Task success on fail

There is an issue, where policy is incorrectly defined, gives fail message task, but task status stays successful.

Run azure/manage-azure-policy@c693881e3b39a41eadc4b74de0ab032a73eeab3b
----------------------------------------------------------------------------------------------------
Failed to get policy with id /subscriptions/#{SubscriptionId}#/providers/Microsoft.Authorization/policyAssignments/<redacted>, path config-files/policy/initiatives/<redacted>/assign.<redacted>.json. Error : {"code":"MissingApiVersionParameter","message":"The api-version query parameter (?api-version=) is required for all requests."}

image

Assign a policy initiatives with role assignment

Hi,

I managed to assign a policy initiative but it doesn't apply the role assignment to the manged identity.
My Policy initiative is referring 4 policies definition and each ones have the role definition ids :

"roleDefinitionIds": [ "/providers/microsoft.authorization/roleDefinitions/749f88d5-cbae-40b8-bcfc-e573ddc772fa", "/providers/microsoft.authorization/roleDefinitions/92aaf0da-9dab-42b6-94a3-d43ce8d16293" ]

My code is here : JamesDLD/azure-polices

The message I got :
image

Thanks in advance,
Jamesdld

Allow policy id to be generated during apply

It is not possible to apply creation of a policy definition without specifying the "id" property in the policy.json file. As the policy id includes either the subscription id or management group id, this means they need to be stored in the repository. This means it is not possible to create a generic set of policy definitions that can be deployed to multiple repositories.

It would be nice if the subscription id or management group id could be specified as a parameter and the policy id generated by combining this with the "name" property from the policy.json file then we can avoid hard-coding subscription or management group ids in the repository.

Unsure of method to un-assign initiative/policy

What process is recommended / intended to un-assign a policy?

Removal of the assign.*.json file from the repo results in no error and no change via the GitHub Action.

Removal of the scope entry in the assignment JSON results in notification of successful update with "Assignment updated successfully" via GitHub Action but no change to the actual assignment. Currently, it appears that the only way to remove an assignment is via a direct API call or portal.

Each export creates a new Service Principal

When exporting to Github repo with the same location, a new service principal is always created. It would be helpful if either the same service principal is identified or there were an option to use an already existing service principal (and workflow file)

PolicySet and Assignment Parameters

Existing action requires parameters are included within policyset.json & assign.*.json. AZ CLI & PowerShell allow parameters to be moved to a separate file to aid reusability.

Request is to:

  • Replicate AZ CLI functionality of "--params" to allow creation of initiative and assignment with specific parameters.

Error occurred while fetching policies from Azure

The initial task worked but subsequent ones return this error with no extra detail. Below is the debug output that doesn't indicate what the issue could be.

##[debug]Evaluating condition for step: 'Create or Update Azure Policies'
##[debug]Evaluating: success()
##[debug]Evaluating success:
##[debug]=> true
##[debug]Result: true
##[debug]Starting: Create or Update Azure Policies
##[debug]Loading inputs
##[debug]Loading env
Run azure/manage-azure-policy@v0
##[debug]Looking for policy definition paths to include...
##[debug]Policy file pattern: policies/**/policy.json
##[debug] Matching policy paths: policies/audit-resource-group-delete-lock/policy.json,policies/audit-sql-backup-retention/policy.json,policies/audit-tag-on-resources/policy.json,policies/storage-account-minimumTlsVersion/policy.json
##[debug]Looking for policy definition paths to ignore...
##[debug]----------------------------------------------------------------------------------------------------
##[debug]Found the following policy paths that match the given path filters:
##[debug]
##[debug]policies/audit-resource-group-delete-lock
##[debug]policies/audit-sql-backup-retention
##[debug]policies/audit-tag-on-resources
##[debug]policies/storage-account-minimumTlsVersion
##[debug]----------------------------------------------------------------------------------------------------
##[debug]Looking for policy initiative paths to include...
##[debug]Policy file pattern: policies/**/policyset.json
##[debug] Matching policy paths: 
##[debug]Looking for policy initiative paths to ignore...
##[debug]----------------------------------------------------------------------------------------------------
##[debug]Found no policy initiatives that match the given path filters.
##[debug]----------------------------------------------------------------------------------------------------
##[debug]Looking for policy assignment paths to include...
##[debug]Assignment pattern: policies/**/assign.*.json
##[debug] Matching assignment paths: 
##[debug]Looking for policy assignment paths to ignore...
##[debug]----------------------------------------------------------------------------------------------------
##[debug]Found no policy assignments that match the given path filters.
##[debug]----------------------------------------------------------------------------------------------------
##[debug]"/usr/bin/az" cloud show
##[debug]"/usr/bin/az" account get-access-token --resource=https://management.azure.com
::add-mask::***
##[debug][POST]https://management.azure.com/batch?api-version=2019-09-01
##[debug]----------------------------------------------------------------------------------------------------
##[debug]Status of batch calls:
##[debug]----------------------------------------------------------------------------------------------------
##[debug]Name : 0. Status : 200
##[debug]Name : 1. Status : 200
##[debug]Name : 2. Status : 200
##[debug]Name : 3. Status : 200
##[debug]----------------------------------------------------------------------------------------------------
##[debug]End
##[debug]----------------------------------------------------------------------------------------------------
##[debug]----------------------------------------------------------------------------------------------------
##[debug]Comparing Hash for policy id : /providers/Microsoft.Management/managementGroups/<REDACTED>/providers/Microsoft.Authorization/policyDefinitions/d94fe135-13ac-42d5-864f-8ad97b9081b6 : true
##[debug]----------------------------------------------------------------------------------------------------
##[debug]----------------------------------------------------------------------------------------------------
##[debug]Comparing Hash for policy id : /providers/Microsoft.Management/managementGroups/<REDACTED>/providers/Microsoft.Authorization/policyDefinitions/2104f3c0-ef2e-4dce-bc77-6c5efdf0d271 : true
##[debug]----------------------------------------------------------------------------------------------------
##[debug]----------------------------------------------------------------------------------------------------
##[debug]Comparing Hash for policy id : /providers/Microsoft.Management/managementGroups/<REDACTED>/providers/Microsoft.Authorization/policyDefinitions/734db12f-b068-424a-8813-f9a4aae8636c : true
##[debug]----------------------------------------------------------------------------------------------------
##[debug]----------------------------------------------------------------------------------------------------
##[debug]Comparing Hash for policy id : /providers/Microsoft.Management/managementGroups/<REDACTED>/providers/Microsoft.Authorization/policyDefinitions/0a8bcc74-a227-46d0-957f-62abb7c98b11 : true
##[debug]----------------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------
Error : Error occurred while fetching policies from Azure.
----------------------------------------------------------------------------------------------------
Error: Error while deploying policies.
##[debug]Node Action run completed with exit code 1
##[debug]AZURE_HTTP_USER_AGENT='GITHUBACTIONS_ManageAzurePolicy_bc9a51e11d1367bd4a7160fd4b830f2ae6f4a6b9b647f660b37511e745ecaff7'
##[debug]Finishing: Create or Update Azure Policies

Reporting a vulnerability

Hello!

I hope you are doing well!

We are a security research team. Our tool automatically detected a vulnerability in this repository. We want to disclose it responsibly. GitHub has a feature called Private vulnerability reporting, which enables security research to privately disclose a vulnerability. Unfortunately, it is not enabled for this repository.

Can you enable it, so that we can report it?

Thanks in advance!

PS: you can read about how to enable private vulnerability reporting here: https://docs.github.com/en/code-security/security-advisories/repository-security-advisories/configuring-private-vulnerability-reporting-for-a-repository

Use in GitLab CI

This could be a feature request, or maybe just a request for information. My company uses Gitlab, not Gihub. So, actions are not available directly. Is there a way to use the Javascript GitHub Actions in this repo from GitLab CI? I've read about an approach for using a Docker GitHub Action by using the Docker image in GitLab CI, but I haven't been able to find an approach for using Javascript Actions in GitLab CI. Is that possible? Thanks.

UserAssigned Identity not supported

Trying to use this action to assign a policy using a UserAssigned MSI which is supported in the portal and via the Azure CLI (as far as I can tell) but when using the action, I receive an error:

The policy assignment <POLICYNAME> request is invalid. The provided identity type 'UserAssigned' is not supported. Supported identity types are 'SystemAssigned, 'None'

This is my identity block (sub, rg and identity name redacted):
"identity": { "type": "UserAssigned", "userAssignedIdentities": { "/subscriptions/{{subscriptionId}}/resourceGroups/{{resourceGroupName}}/providers/Microsoft.ManagedIdentity/userAssignedIdentities/{{UserAssignedIdentityName}}": {} }

Based on this documentation: https://docs.microsoft.com/en-us/azure/templates/microsoft.authorization/policyassignments?tabs=json#policyassignmentproperties

Are there plans to support UserAssigned identities in this action?

Error: Table data must not contain control characters - Policy assignment deployment

I cant seem to get the attached policy assignment deployed in my tenant. The file is json valid and as far as i can tell correctly defined.

The error i get from the action is like the issue topic says: Error: Table data must not contain control characters.
All other assignments seem to work as expected.

I cannot find any control characters, please let me know if i can do anything to help.

Attached is the policy and assignment file. The policy got deployed fine.

Policy Assignment

{
  "sku": {
    "name": "A0",
    "tier": "Free"
  },
  "identity": {
    "type": "None"
  },
  "properties": {
    "displayName": "Deploy-Budget",
    "policyDefinitionId": "/providers/Microsoft.Management/managementGroups/gitopslab/providers/Microsoft.Authorization/policyDefinitions/Deploy-Budget",
    "scope": "/providers/Microsoft.Management/managementGroups/gitopslab",
    "notScopes": [
      "/providers/Microsoft.Management/managementGroups/gitopslab/lab"
    ],
    "parameters": {
      "amount": {
        "value": "50000"
      },
      "timeGrain": {
        "value": "Monthly"
      },
      "firstThreshold": {
        "value": "75"
      },
      "secondThreshold": {
        "value": "90"
      },
      "contactRoles": [
        "Owner",
        "Contributor"
      ],
      "contactEmails": [
        "[email protected]"
      ],
      "contactGroups": []
    },
    "description": "Deploy a default budget on all subscriptions under the assigned scope",
    "metadata": {
      "assignedBy": "test team"
    },
    "enforcementMode": "Default"
  },
  "id": "/providers/Microsoft.Management/managementGroups/gitopslab/providers/Microsoft.Authorization/policyAssignments/Deploy-Budget",
  "type": "Microsoft.Authorization/policyAssignments",
  "name": "Deploy-Budget",
  "location": "norwayeast"
}

Policy object

{
  "name": "Deploy-Budget",
  "type": "Microsoft.Authorization/policyDefinitions",
  "properties": {
    "policyType": "Custom",
    "mode": "All",
    "displayName": "Deploy a default budget on all subscriptions under the assigned scope",
    "description": "Deploy a default budget on all subscriptions under the assigned scope",
    "metadata": {
      "version": "1.0.0",
      "category": "Budget"
    },
    "parameters": {
      "amount": {
        "type": "String",
        "defaultValue": "1000",
        "metadata": {
          "description": "The total amount of cost or usage to track with the budget"
        }
      },
      "timeGrain": {
        "type": "String",
        "defaultValue": "Monthly",
        "allowedValues": [
          "Monthly",
          "Quarterly",
          "Annually",
          "BillingMonth",
          "BillingQuarter",
          "BillingAnnual"
        ],
        "metadata": {
          "description": "The time covered by a budget. Tracking of the amount will be reset based on the time grain."
        }
      },
      "firstThreshold": {
        "type": "String",
        "defaultValue": "90",
        "metadata": {
          "description": "Threshold value associated with a notification. Notification is sent when the cost exceeded the threshold. It is always percent and has to be between 0 and 1000."
        }
      },
      "secondThreshold": {
        "type": "String",
        "defaultValue": "100",
        "metadata": {
          "description": "Threshold value associated with a notification. Notification is sent when the cost exceeded the threshold. It is always percent and has to be between 0 and 1000."
        }
      },
      "contactRoles": {
        "type": "Array",
        "defaultValue": [
          "Owner",
          "Contributor"
        ],
        "metadata": {
          "description": "The list of contact RBAC roles, in an array, to send the budget notification to when the threshold is exceeded."
        }
      },
      "contactEmails": {
        "type": "Array",
        "defaultValue": [],
        "metadata": {
          "description": "The list of email addresses, in an array, to send the budget notification to when the threshold is exceeded."
        }
      },
      "contactGroups": {
        "type": "Array",
        "defaultValue": [],
        "metadata": {
          "description": "The list of action groups, in an array, to send the budget notification to when the threshold is exceeded. It accepts array of strings."
        }
      }
    },
    "policyRule": {
      "if": {
        "allOf": [
          {
            "field": "type",
            "equals": "Microsoft.Resources/subscriptions"
          }
        ]
      },
      "then": {
        "effect": "DeployIfNotExists",
        "details": {
          "type": "Microsoft.Consumption/budgets",
          "deploymentScope": "Subscription",
          "existenceScope": "Subscription",
          "existenceCondition": {
            "allOf": [
              {
                "field": "Microsoft.Consumption/budgets/amount",
                "equals": "[parameters('amount')]"
              },
              {
                "field": "Microsoft.Consumption/budgets/timeGrain",
                "equals": "[parameters('timeGrain')]"
              },
              {
                "field": "Microsoft.Consumption/budgets/category",
                "equals": "Cost"
              }
            ]
          },
          "roleDefinitionIds": [
            "/providers/Microsoft.Authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c"
          ],
          "deployment": {
            "location": "norwayeast",
            "properties": {
              "mode": "Incremental",
              "parameters": {
                "amount": {
                  "value": "[parameters('amount')]"
                },
                "timeGrain": {
                  "value": "[parameters('timeGrain')]"
                },
                "firstThreshold": {
                  "value": "[parameters('firstThreshold')]"
                },
                "secondThreshold": {
                  "value": "[parameters('secondThreshold')]"
                },
                "contactEmails": {
                  "value": "[parameters('contactEmails')]"
                },
                "contactRoles": {
                  "value": "[parameters('contactRoles')]"
                },
                "contactGroups": {
                  "value": "[parameters('contactGroups')]"
                }
              },
              "template": {
                "$schema": "http://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json",
                "contentVersion": "1.0.0.0",
                "parameters": {
                  "amount": {
                    "type": "String"
                  },
                  "timeGrain": {
                    "type": "String"
                  },
                  "firstThreshold": {
                    "type": "String"
                  },
                  "secondThreshold": {
                    "type": "String"
                  },
                  "contactEmails": {
                    "type": "Array"
                  },
                  "contactRoles": {
                    "type": "Array"
                  },
                  "contactGroups": {
                    "type": "Array"
                  },
                  "startDate": {
                    "type": "String",
                    "defaultValue": "[concat(utcNow('MM'), '/01/', utcNow('yyyy'))]"
                  }
                },
                "resources": [
                  {
                    "type": "Microsoft.Consumption/budgets",
                    "apiVersion": "2019-10-01",
                    "name": "default-sandbox-budget",
                    "properties": {
                      "timePeriod": {
                        "startDate": "[parameters('startDate')]"
                      },
                      "timeGrain": "[parameters('timeGrain')]",
                      "amount": "[parameters('amount')]",
                      "category": "Cost",
                      "notifications": {
                        "NotificationForExceededBudget1": {
                          "enabled": true,
                          "operator": "GreaterThan",
                          "threshold": "[parameters('firstThreshold')]",
                          "contactEmails": "[parameters('contactEmails')]",
                          "contactRoles": "[parameters('contactRoles')]",
                          "contactGroups": "[parameters('contactGroups')]"
                        },
                        "NotificationForExceededBudget2": {
                          "enabled": true,
                          "operator": "GreaterThan",
                          "threshold": "[parameters('secondThreshold')]",
                          "contactEmails": "[parameters('contactEmails')]",
                          "contactRoles": "[parameters('contactRoles')]",
                          "contactGroups": "[parameters('contactGroups')]"
                        }
                      }
                    }
                  }
                ]
              }
            }
          }
        }
      }
    }
  },
  "id": "/providers/Microsoft.Management/managementGroups/gitopslab/providers/Microsoft.Authorization/policyDefinitions/Deploy-Budget"
}

Debug output from the Github Action "Create or Update Azure Policies" step.

##[debug]Evaluating condition for step: 'Create or Update Azure Policies'
##[debug]Evaluating: success()
##[debug]Evaluating success:
##[debug]=> true
##[debug]Result: true
##[debug]Starting: Create or Update Azure Policies
##[debug]Loading inputs
##[debug]Loading env
Run azure/manage-azure-policy@v0
##[debug]Looking for policy definition paths to include...
##[debug]Policy file pattern: policies/**/policy.json
##[debug] Matching policy paths: policies/allowed_locations/policy.json,policies/append_appservice_httpsonly/policy.json,policies/append_appservice_latesttls/policy.json,policies/deny_appserviceapiapp_http/policy.json,policies/deny_appservicefunctionapp_http/policy.json,policies/deny_appservicewebapp_http/policy.json,policies/deny_publicip/policy.json,policies/deploy_budget/policy.json
##[debug]Looking for policy definition paths to ignore...
##[debug]----------------------------------------------------------------------------------------------------
##[debug]Found the following policy paths that match the given path filters:
##[debug]
##[debug]policies/allowed_locations
##[debug]policies/append_appservice_httpsonly
##[debug]policies/append_appservice_latesttls
##[debug]policies/deny_appserviceapiapp_http
##[debug]policies/deny_appservicefunctionapp_http
##[debug]policies/deny_appservicewebapp_http
##[debug]policies/deny_publicip
##[debug]policies/deploy_budget
##[debug]----------------------------------------------------------------------------------------------------
##[debug]Looking for policy initiative paths to include...
##[debug]Policy file pattern: policies/**/policyset.json
##[debug] Matching policy paths: 
##[debug]Looking for policy initiative paths to ignore...
##[debug]----------------------------------------------------------------------------------------------------
##[debug]Found no policy initiatives that match the given path filters.
##[debug]----------------------------------------------------------------------------------------------------
##[debug]Looking for policy assignment paths to include...
##[debug]Assignment pattern: policies/**/assign.*.json
##[debug] Matching assignment paths: policies/allowed_locations/assign.allowed_locations.json,policies/append_appservice_httpsonly/assign.append_appservice_httpsonly_root.json,policies/append_appservice_latesttls/assign.append_appservice_latesttls_root.json,policies/deny_appserviceapiapp_http/assign.deny_appserviceapiapp_http_root.json,policies/deny_appservicefunctionapp_http/assign.deny_appservicefunctionapp_http_root.json,policies/deny_appservicewebapp_http/assign.deny_appservicewebapp_http_root.json,policies/deny_publicip/assign.deny_publicip.json,policies/deploy_budget/assign.deploy_budget_root.json
##[debug]Looking for policy assignment paths to ignore...
##[debug]----------------------------------------------------------------------------------------------------
##[debug]Found the following policy assignment paths that match the given path filters:
##[debug]
##[debug]policies/allowed_locations/assign.allowed_locations.json
##[debug]policies/append_appservice_httpsonly/assign.append_appservice_httpsonly_root.json
##[debug]policies/append_appservice_latesttls/assign.append_appservice_latesttls_root.json
##[debug]policies/deny_appserviceapiapp_http/assign.deny_appserviceapiapp_http_root.json
##[debug]policies/deny_appservicefunctionapp_http/assign.deny_appservicefunctionapp_http_root.json
##[debug]policies/deny_appservicewebapp_http/assign.deny_appservicewebapp_http_root.json
##[debug]policies/deny_publicip/assign.deny_publicip.json
##[debug]policies/deploy_budget/assign.deploy_budget_root.json
##[debug]----------------------------------------------------------------------------------------------------
##[debug]----------------------------------------------------------------------------------------------------
##[debug]Ignoring policy with BuiltIn type. Id : /providers/Microsoft.Management/managementGroups/gitopslab/providers/Microsoft.Authorization/policyDefinitions/e56962a6-4747-49cd-b67b-bf8b01975c4c, path : policies/allowed_locations
##[debug]----------------------------------------------------------------------------------------------------
##[debug]Checking if assignment path 'policies/allowed_locations/assign.allowed_locations.json' is set to do not enforce
##[debug]Checking if assignment path 'policies/allowed_locations/assign.allowed_locations.json' is set to enforce
##[debug]Checking if assignment path 'policies/append_appservice_httpsonly/assign.append_appservice_httpsonly_root.json' is set to do not enforce
##[debug]Checking if assignment path 'policies/append_appservice_httpsonly/assign.append_appservice_httpsonly_root.json' is set to enforce
##[debug]Checking if assignment path 'policies/append_appservice_latesttls/assign.append_appservice_latesttls_root.json' is set to do not enforce
##[debug]Checking if assignment path 'policies/append_appservice_latesttls/assign.append_appservice_latesttls_root.json' is set to enforce
##[debug]Checking if assignment path 'policies/deny_appserviceapiapp_http/assign.deny_appserviceapiapp_http_root.json' is set to do not enforce
##[debug]Checking if assignment path 'policies/deny_appserviceapiapp_http/assign.deny_appserviceapiapp_http_root.json' is set to enforce
##[debug]Checking if assignment path 'policies/deny_appservicefunctionapp_http/assign.deny_appservicefunctionapp_http_root.json' is set to do not enforce
##[debug]Checking if assignment path 'policies/deny_appservicefunctionapp_http/assign.deny_appservicefunctionapp_http_root.json' is set to enforce
##[debug]Checking if assignment path 'policies/deny_appservicewebapp_http/assign.deny_appservicewebapp_http_root.json' is set to do not enforce
##[debug]Checking if assignment path 'policies/deny_appservicewebapp_http/assign.deny_appservicewebapp_http_root.json' is set to enforce
##[debug]Checking if assignment path 'policies/deny_publicip/assign.deny_publicip.json' is set to do not enforce
##[debug]Checking if assignment path 'policies/deny_publicip/assign.deny_publicip.json' is set to enforce
##[debug]Checking if assignment path 'policies/deploy_budget/assign.deploy_budget_root.json' is set to do not enforce
##[debug]Checking if assignment path 'policies/deploy_budget/assign.deploy_budget_root.json' is set to enforce
##[debug]"/usr/bin/az" cloud show
##[debug]"/usr/bin/az" account get-access-token --resource=https://management.azure.com
::add-mask::***
##[debug][POST]https://management.azure.com/batch?api-version=2020-09-01
##[debug]----------------------------------------------------------------------------------------------------
##[debug]Status of batch calls:
##[debug]----------------------------------------------------------------------------------------------------
##[debug]Name : 0. Status : 200
##[debug]Name : 1. Status : 200
##[debug]Name : 2. Status : 200
##[debug]Name : 3. Status : 200
##[debug]Name : 4. Status : 200
##[debug]Name : 5. Status : 200
##[debug]Name : 6. Status : 200
##[debug]Name : 7. Status : 200
##[debug]Name : 8. Status : 200
##[debug]Name : 9. Status : 200
##[debug]Name : 10. Status : 200
##[debug]Name : 11. Status : 200
##[debug]Name : 12. Status : 200
##[debug]Name : 13. Status : 200
##[debug]Name : 14. Status : 404
##[debug]----------------------------------------------------------------------------------------------------
##[debug]End
##[debug]----------------------------------------------------------------------------------------------------
##[debug]----------------------------------------------------------------------------------------------------
##[debug]Comparing Hash for policy id : /providers/Microsoft.Management/managementGroups/gitopslab/providers/Microsoft.Authorization/policyDefinitions/Append-AppService-httpsonly : true
##[debug]----------------------------------------------------------------------------------------------------
##[debug]----------------------------------------------------------------------------------------------------
##[debug]Comparing Hash for policy id : /providers/Microsoft.Management/managementGroups/gitopslab/providers/Microsoft.Authorization/policyDefinitions/Append-AppService-latestTLS : true
##[debug]----------------------------------------------------------------------------------------------------
##[debug]----------------------------------------------------------------------------------------------------
##[debug]Comparing Hash for policy id : /providers/Microsoft.Management/managementGroups/gitopslab/providers/Microsoft.Authorization/policyDefinitions/Deny-AppServiceApiApp-http : true
##[debug]----------------------------------------------------------------------------------------------------
##[debug]----------------------------------------------------------------------------------------------------
##[debug]Comparing Hash for policy id : /providers/Microsoft.Management/managementGroups/gitopslab/providers/Microsoft.Authorization/policyDefinitions/Deny-AppServiceFunctionApp-http : true
##[debug]----------------------------------------------------------------------------------------------------
##[debug]----------------------------------------------------------------------------------------------------
##[debug]Comparing Hash for policy id : /providers/Microsoft.Management/managementGroups/gitopslab/providers/Microsoft.Authorization/policyDefinitions/Deny-AppServiceWebApp-http : true
##[debug]----------------------------------------------------------------------------------------------------
##[debug]----------------------------------------------------------------------------------------------------
##[debug]Comparing Hash for policy id : /providers/Microsoft.Management/managementGroups/gitopslab/providers/Microsoft.Authorization/policyDefinitions/Deny-PublicIP : true
##[debug]----------------------------------------------------------------------------------------------------
##[debug]----------------------------------------------------------------------------------------------------
##[debug]Comparing Hash for policy id : /providers/Microsoft.Management/managementGroups/gitopslab/providers/Microsoft.Authorization/policyDefinitions/Deploy-Budget : true
##[debug]----------------------------------------------------------------------------------------------------
##[debug]----------------------------------------------------------------------------------------------------
##[debug]Comparing Hash for policy id : /providers/Microsoft.Management/managementGroups/gitopslab/providers/Microsoft.Authorization/policyAssignments/GO-Allowed-Locations : true
##[debug]----------------------------------------------------------------------------------------------------
##[debug]----------------------------------------------------------------------------------------------------
##[debug]Comparing Hash for policy id : /providers/Microsoft.Management/managementGroups/gitopslab/providers/Microsoft.Authorization/policyAssignments/Append-AppServ-httpson : true
##[debug]----------------------------------------------------------------------------------------------------
##[debug]----------------------------------------------------------------------------------------------------
##[debug]Comparing Hash for policy id : /providers/Microsoft.Management/managementGroups/gitopslab/providers/Microsoft.Authorization/policyAssignments/Append-AppServ-latestT : true
##[debug]----------------------------------------------------------------------------------------------------
##[debug]----------------------------------------------------------------------------------------------------
##[debug]Comparing Hash for policy id : /providers/Microsoft.Management/managementGroups/gitopslab/providers/Microsoft.Authorization/policyAssignments/Deny-AppSvcAPIApp-http : true
##[debug]----------------------------------------------------------------------------------------------------
##[debug]----------------------------------------------------------------------------------------------------
##[debug]Comparing Hash for policy id : /providers/Microsoft.Management/managementGroups/gitopslab/providers/Microsoft.Authorization/policyAssignments/Deny-AppSvcFuncApp-http : true
##[debug]----------------------------------------------------------------------------------------------------
##[debug]----------------------------------------------------------------------------------------------------
##[debug]Comparing Hash for policy id : /providers/Microsoft.Management/managementGroups/gitopslab/providers/Microsoft.Authorization/policyAssignments/Deny-AppSvcWebApp-http : true
##[debug]----------------------------------------------------------------------------------------------------
##[debug]----------------------------------------------------------------------------------------------------
##[debug]Comparing Hash for policy id : /providers/Microsoft.Management/managementGroups/gitopslab/providers/Microsoft.Authorization/policyAssignments/GO-Deny-PublicIP : true
##[debug]----------------------------------------------------------------------------------------------------
##[debug]----------------------------------------------------------------------------------------------------
##[debug]Policy with id : /providers/Microsoft.Management/managementGroups/gitopslab/providers/Microsoft.Authorization/policyAssignments/Deploy-Budget, path : policies/deploy_budget/assign.deploy_budget_root.json does not exist in azure. A new policy will be created.
##[debug]----------------------------------------------------------------------------------------------------
##[debug]"/usr/bin/az" account get-access-token --resource=https://management.azure.com
::add-mask::***
##[debug][POST]https://management.azure.com/batch?api-version=2020-09-01
##[debug]----------------------------------------------------------------------------------------------------
##[debug]Status of batch calls:
##[debug]----------------------------------------------------------------------------------------------------
##[debug]Name : 0. Status : 400
##[debug]----------------------------------------------------------------------------------------------------
##[debug]End
##[debug]----------------------------------------------------------------------------------------------------
##[debug]"/usr/bin/az" account get-access-token --resource=https://management.azure.com
::add-mask::***
##[debug]----------------------------------------------------------------------------------------------------
##[debug]No role assignments needs to be created
##[debug]----------------------------------------------------------------------------------------------------
Error: Table data must not contain control characters.
----------------------------------------------------------------------------------------------------
Error : Error: Table data must not contain control characters.
----------------------------------------------------------------------------------------------------
Error: Found '1' failure(s) while deploying policies.
##[debug]Node Action run completed with exit code 1
##[debug]AZURE_HTTP_USER_AGENT='**'
##[debug]Finishing: Create or Update Azure Policies

Policy remediation will not work after policy is deployed via manage-azure-policy action

Steps to reproduce:

  1. Create Azure policy definition and assignment in Portal - policy effect has to support remediation, e.g. DeployIfNotExists
  2. Export both into GitHub from Portal
  3. Delete Azure policy definition and assignment in Portal, so that manage azure policy action has to setup everything from scratch
  4. Run manage azure policy action, which will create the policy definition and assignment from scratch
  5. Remediate one of the policies will cause the following error message:
Error message you get during policy remediation: "The client '<MANAGED_IDENTITY_USED_FOR_REMEDIATION>' with object id '<MANAGED_IDENTITY_USED_FOR_REMEDIATION>' does not have authorization to perform action 'Microsoft.Resources/deployments/validate/action'
 ver scope '/subscriptions/<SUBSCRIPTION_ID>/resourcegroups/1808a0d8-3365-4f66-bcd3-9c64625521c1/providers/Microsoft.Resources/deployments/PolicyDeployment_16080442889105320296' or the scope is invalid.
If access was recently granted, please refresh your credentials."

(Managed identity and subscription id are masked for privacy reasons)

Root cause analysis:
When policies are assigned with remediation, a managed identity is required to actually execute that remediation (i.e. DeployIfNotExists, Modify, etc. ). Hence the respective roles have to be assigned to that managed identity. When you look at the underlying REST API requests performed by Azure Portal or Azure PowerShell (New-AzPolicyAssignment ... -AssignIdentity -Debug) during policy assignment, additional requests are sent to setup the role assignments of the managed identity. On first glance, I could not find that additional request being sent out in the source code of the manage-azure-policy action (well-written code btw. 🥇 ). Also, when manually assigning the required role to the managed identity, the policy remediation works:

$managedIdentity = (Get-AzPolicyAssignment | Where-Object { $_.Properties.DisplayName -eq "<POLICY_NAME>"  }).Identity
New-AzRoleAssignment -ObjectId $identity.principalId -RoleDefinitionName "Network Contributor" -Scope /subscriptions/<SUBSCRIPTION_ID>

(Subscription id is masked for privacy reasons)

Finally, I also sent a sample policy definition and assignment to @raiyanalam to ease reproduction.

Actions reports success when ARM API return error

Hello,
I have noticed the action report success status when ARM API call fails (at least for assignment).
No conditionals are used for the action.

Bellow two examples:

Failed to get policy with id /providers/Microsoft.Management/managementGroups/test-spgi-terraform/providers/Microsoft.Authorization/policyAssignments/Append_NSG_if_missing-4ca69cc5-3a14-4614-9d17-04e0ca2cee70, path config-files/policy/policies/Append_NSG_for_MG/assign.Test-SPGI-Terraform.Append_NSG_if_missing.json. Error : {"code":"InvalidPolicyAssignmentName","message":"The policy assignment name 'Append_NSG_if_missing-4ca69cc5-3a14-4614-9d17-04e0ca2cee70' is invalid. The policy assignment name length must not exceed '24' characters."}

image
image

Could not create role assignment to the managed identity of a Policy Initiative

When deploying a Policy Set definition referencing Policy definitions with the "DeployIfNotExists", I get the following:

##[debug]----------------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------
Could not find role definition ids for adding role assignments to the managed identity. Definition Id : /providers/Microsoft.Management/managementGroups/ES/providers/Microsoft.Authorization/policySetDefinitions/Enforce-EncryptTransit
----------------------------------------------------------------------------------------------------
##[debug]----------------------------------------------------------------------------------------------------
##[debug]No role assignments needs to be created
##[debug]----------------------------------------------------------------------------------------------------

It seems it is trying to find a "roleDefinitionIds": in the policySet Definition. The managed identity of the initiative's assignment gets created but without any role assignment.
If I try to deploy an assignment of the referenced policy, the created managed identity for the policy assignment has the defined role assigned - as expected.

Can you please check ?

Policy Files Not Being Detected

Hi, I've created the following:

/root
../policies
../../check-storage-account-location
../../../assign.DEV.json
../../../policy.json

Yet the action reports:

Warning: Did not find any policies to create/update. No policy files match the given patterns or no changes were detected. If you have policy definitions or policy initiatives, please ensure that the files are named 'policy.json' and 'policyset.json' respectively.

... and no policy is created in my subscription. Can someone double check the dir/file setup above it correct ? Is there anything within the policy/assignment files themselves that might stop the action from working?

I'm using the action as follows:

      - name: Create or Update Azure Policies
        uses: azure/manage-azure-policy@v0
        with:      
          paths: |                  
            policies/**  # path to directory where policy files were downloaded in runner

Multi-tenant support

I have a requirement to be able to deploy the same policysets/initives to multiple tenants. Is that something that this action can support or be easily modified to support?

Delete policy and assignment when the files are missing

Hi!

From what i can read in the documentation provided here it seems the manage-azure-policy action does not automaticly delete azure policy definitions and assignments from the portal when they are removed from the policies/** path in the source repo.

Is this by design or am i missing something?

I have tried to remove the files containing the definitions and assignments before running the action in both incremental and complete mode.

If this feature is not implemented it would be greatly appreciated, as it makes handling the policy as a code aspect much easier.

Best regards
William

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.