|
| 1 | +--- |
| 2 | +title: Automating app installations in your enterprise's organizations |
| 3 | +intro: Automate a process consistently across organizations by creating a {% data variables.product.prodname_github_app %} in your enterprise and installing it programmatically. |
| 4 | +versions: |
| 5 | + feature: enterprise-installed-apps |
| 6 | +contentType: tutorials |
| 7 | +shortTitle: Automate installations |
| 8 | +--- |
| 9 | + |
| 10 | +## Introduction |
| 11 | + |
| 12 | +To automate a process securely, you can create a {% data variables.product.prodname_github_app %} owned by an enterprise account, then install the app in the enterprise or organization where the automation will take place. |
| 13 | + |
| 14 | +{% data variables.product.prodname_github_apps %} provide tokens that you can use to authenticate API calls to {% data variables.product.github %} in scripts and workflows. These tokens are suitable for enterprises with specific security and auditing requirements, because they are: |
| 15 | + |
| 16 | +* Temporary |
| 17 | +* Scoped to specific accounts and permissions |
| 18 | +* Associated with the app's identity rather than a user account |
| 19 | + |
| 20 | +A common need for large enterprises is to keep an automation consistent and up-to-date across many organizations. You can accomplish this by installing an app programmatically. For example, if you need to configure all organizations with certain policies and settings, you could install a {% data variables.product.prodname_github_app %} for this task in every organization. |
| 21 | + |
| 22 | +This guide will demonstrate the steps required to programmatically install an enterprise-owned {% data variables.product.prodname_github_app %} in an organization. Once the app is installed, you'll use it to create a new repository. |
| 23 | + |
| 24 | +## Overview of the process |
| 25 | + |
| 26 | +In this guide, you'll use the {% data variables.product.prodname_cli %} to make the API calls required to request access tokens and install an app in an organization. In reality, this process would be part of a custom script tailored to your company's needs. |
| 27 | + |
| 28 | +You will: |
| 29 | + |
| 30 | +1. Create two apps owned by your enterprise account. |
| 31 | + * One will have permission to install apps in organizations. |
| 32 | + * The other will have permission to automate a process in an organization (in this case, creating a repository). |
| 33 | +1. Authenticate the first app to obtain an **enterprise-scoped** access token. |
| 34 | +1. Use the enterprise-scoped token to call an API that installs the automation app in an organization. |
| 35 | +1. Authenticate the organization-installed app to obtain an **organization-scoped** access token. |
| 36 | +1. Use the organization-scoped token to call an API that creates a repository in the organization. |
| 37 | + |
| 38 | +At each stage, you will use a token that only has permission to perform specific actions in a specific account. From a security and auditing perspective, this approach is superior to relying on a single token with permission to perform actions across your enterprise and organizations. |
| 39 | + |
| 40 | +## Prerequisites |
| 41 | + |
| 42 | +To follow this guide on your own device, you must: |
| 43 | + |
| 44 | +* Be an enterprise owner. |
| 45 | +* Be an owner of an enterprise-owned organization where you will perform the automation. |
| 46 | +* Have the {% data variables.product.prodname_cli %} installed for making API calls. See [Installation](https://github.com/cli/cli?tab=readme-ov-file#installation) in the {% data variables.product.prodname_cli %} repository. |
| 47 | +* Have `openssl` installed in order to generate a JSON web token (JWT). Many devices have OpenSSL installed by default. You can check by running `openssl -v`, which returns a version number if installed. |
| 48 | +* Use a Unix shell such as Bash, ZSH, or Git Bash. |
| 49 | + |
| 50 | +## 1. Prepare to generate a JWT |
| 51 | + |
| 52 | +To request an access token from an app, you need a JSON web token (JWT) generated from the app's client ID and private key. Many programming languages have built-in methods for generating a JWT. In this tutorial, you will use a Bash script to generate a JWT from the command line using `openssl`. |
| 53 | + |
| 54 | +1. Copy the contents of the Bash script for generating a JWT from [AUTOTITLE](/apps/creating-github-apps/authenticating-with-a-github-app/generating-a-json-web-token-jwt-for-a-github-app#example-using-bash-to-generate-a-jwt). |
| 55 | +1. Save the contents in a new file in your home directory, called `gen-jwt.sh`. |
| 56 | +1. Make the script executable. |
| 57 | + |
| 58 | + ```shell copy |
| 59 | + chmod +x ~/gen-jwt.sh |
| 60 | + ``` |
| 61 | + |
| 62 | +## 2. Create two enterprise apps |
| 63 | + |
| 64 | +{% data variables.product.prodname_github_apps %} can only generate tokens with the fine-grained permissions and account scopes you allow. In this step, you will create two enterprise-owned apps: one with **enterprise-level** permission to install apps, and another with **organization-level** permission to create a repository. |
| 65 | + |
| 66 | +### a. Create the installer app |
| 67 | + |
| 68 | +The "installer app" will be installed on the enterprise account, and will have permission to install the other app in an organization. |
| 69 | + |
| 70 | +1. Create a new app under your enterprise account. |
| 71 | + |
| 72 | + 1. Go to your enterprise account settings. |
| 73 | + 1. In the left sidebar, click **{% data variables.product.prodname_github_apps %}**. |
| 74 | + 1. Click **New {% data variables.product.prodname_github_app %}**. |
| 75 | + |
| 76 | +1. You can leave most of the values as defaults, except: |
| 77 | + |
| 78 | + * Call the app `YOUR-HANDLE-installer-app`, replacing `YOUR-HANDLE` with your {% data variables.product.github %} username. |
| 79 | + * Set the "Homepage URL" to `https://github.com`. |
| 80 | + * Deselect **Expire user authorization tokens** and, under "Webhook", deselect **Active**. |
| 81 | + * Under "Enterprise permissions", give the app read and write permissions for **Enterprise organization installations**. |
| 82 | + |
| 83 | +1. After creating the app, copy the **Client ID** and save it as a variable called `INSTALLER_APP_CLIENT_ID`. For example, on the command line: |
| 84 | + |
| 85 | + ``` shell copy |
| 86 | + export INSTALLER_APP_CLIENT_ID='abcde12345' |
| 87 | + ``` |
| 88 | + |
| 89 | +1. On the app page, scroll down and click **Generate a private key**. The private key file will be downloaded. |
| 90 | +1. Make a note of the file path, which will look something like `~/YOUR-HANDLE-installer-app.DOWNLOAD-DATE.private-key.pem`. |
| 91 | +1. In the left sidebar of the app page, click **Install app**, then install the new app on the enterprise account. Installing an app gives the app permission to perform actions in the account. |
| 92 | +1. Look at the URL in your browser to find the app's installation ID. This is a string of numbers at the end of the `/enterprises/ENTERPRISE/settings/installations/ID` URL. Save this as the `INSTALLER_APP_INSTALL_ID` variable. |
| 93 | + |
| 94 | + ``` shell copy |
| 95 | + export INSTALLER_APP_INSTALL_ID='12345678' |
| 96 | + ``` |
| 97 | + |
| 98 | +1. Save the name of the enterprise where the app is installed as a variable. |
| 99 | + |
| 100 | + ``` shell copy |
| 101 | + export ENTERPRISE='octo-enterprise' |
| 102 | + ``` |
| 103 | + |
| 104 | +### b. Create the automation app |
| 105 | + |
| 106 | +The "automation app" will be installed in an organization, and will have permission to create repositories in that organization. In reality, you would give this app whatever permissions are required to automate a process in your organizations. |
| 107 | + |
| 108 | +1. Create a new app under your enterprise account. |
| 109 | + * Call the app `YOUR-HANDLE-automation-app`, replacing `YOUR-HANDLE` with your {% data variables.product.github %} username. |
| 110 | + * Set the "Homepage URL" to `https://github.com`. |
| 111 | + * Deselect **Expire user authorization tokens** and, under "Webhook", deselect **Active**. |
| 112 | + * Under "Repository permissions", give the app read and write permissions for **Administration**. |
| 113 | +1. After creating the app, copy the **Client ID** and save it as the `AUTOMATION_APP_CLIENT_ID` variable. |
| 114 | + |
| 115 | + ``` shell copy |
| 116 | + export AUTOMATION_APP_CLIENT_ID='abcde12345' |
| 117 | + ``` |
| 118 | + |
| 119 | +1. Scroll down and click **Generate a private key**. The private key file will be downloaded. |
| 120 | +1. Make a note of the file path, which will look something like `~/YOUR-HANDLE-automation-app.DOWNLOAD-DATE.private-key.pem`. |
| 121 | +1. Save the name of the organization where the app will be installed as a variable. |
| 122 | + |
| 123 | + ``` shell copy |
| 124 | + export ORG='octo-org' |
| 125 | + ``` |
| 126 | + |
| 127 | +## 3. Authenticate the installer app |
| 128 | + |
| 129 | +Authenticating an app allows you to obtain a token with the scope and permissions you defined when you registered the app. In this case, you will obtain a token for the installer app, which will give you permission to install the automation app in an organization. |
| 130 | + |
| 131 | +1. Generate a JWT using the Bash script you saved. For example: |
| 132 | + |
| 133 | + ``` shell copy |
| 134 | + ~/gen-jwt.sh $INSTALLER_APP_CLIENT_ID ~/YOUR-HANDLE-installer-app.DOWNLOAD-DATE.private-key.pem |
| 135 | + ``` |
| 136 | + |
| 137 | +1. Copy the JWT (the long string after `JWT= `) and save it as a variable. |
| 138 | + |
| 139 | + ``` shell copy |
| 140 | + export INSTALL_JWT='abcde12345' |
| 141 | + ``` |
| 142 | + |
| 143 | +1. Use the JWT to authenticate a request for an installation access token. This step uses the [Create an installation access token for an app](/rest/apps/apps#create-an-installation-access-token-for-an-app) API endpoint and requires the app's installation ID. |
| 144 | + |
| 145 | + ```shell copy |
| 146 | + gh api --method POST "/app/installations/$INSTALLER_APP_INSTALL_ID/access_tokens" --header "Authorization: Bearer $INSTALL_JWT" |
| 147 | + ``` |
| 148 | + |
| 149 | +1. You should see a JSON object containing a `token` property. Copy the access token (the value of the `token` property, without quotes) and save it as the `INSTALLER_APP_INSTALL_TOKEN` variable. |
| 150 | + |
| 151 | + ``` shell copy |
| 152 | + export INSTALLER_APP_INSTALL_TOKEN='abcde12345' |
| 153 | + ``` |
| 154 | + |
| 155 | +## 4. Install the automation app |
| 156 | + |
| 157 | +The installation access token you just received gives you permission to call the API for installing apps in an organization. Here, we will use the token to install the automation app (the second app you created) in a specific organization. In reality, you could call this API multiple times to install an app in multiple organizations. |
| 158 | + |
| 159 | +1. Run the following command. We're using the [Install a GitHub App on an enterprise-owned organization](/rest/enterprise-admin/organization-installations#install-a-github-app-on-an-enterprise-owned-organization) API endpoint, authenticating with the installation token you just requested, and passing the client ID of the automation app that we want to install. |
| 160 | + |
| 161 | + ```shell copy |
| 162 | + gh api --method POST \ |
| 163 | + "/enterprises/$ENTERPRISE/apps/organizations/$ORG/installations" \ |
| 164 | + --header "Authorization: Bearer $INSTALLER_APP_INSTALL_TOKEN" \ |
| 165 | + --header "Accept: application/vnd.github+json" \ |
| 166 | + --header "X-GitHub-Api-Version: 2022-11-28" \ |
| 167 | + --field "client_id=$AUTOMATION_APP_CLIENT_ID" \ |
| 168 | + --field "repository_selection=all" |
| 169 | + ``` |
| 170 | + |
| 171 | +1. If successful, you should see a large number of properties returned, starting with the app's installation ID. |
| 172 | +
|
| 173 | + To check the app was successfully installed, go to `https://github.com/organizations/ORG/settings/installations`, replacing ORG with the organization name. You should see the newly installed app on the page. |
| 174 | +
|
| 175 | +1. Find the installation ID of the new installation, and save it as `AUTOMATION_APP_INSTALL_ID`. To find the ID, you can either copy the first ID property returned by the API, or click **Configure** next to the app installation in the UI and copy the ID from the URL. |
| 176 | +
|
| 177 | + ``` shell copy |
| 178 | + export AUTOMATION_APP_INSTALL_ID='12345678' |
| 179 | + ``` |
| 180 | +
|
| 181 | +## 5. Authenticate the automation app |
| 182 | +
|
| 183 | +Just as you authenticated the installer app to obtain an enterprise-scoped token, you now need to perform the same process for the automation app. This will give you an organization-scoped token with permission to create repositories. |
| 184 | +
|
| 185 | +1. Generate a JWT with the automation app's client ID and private key. For example: |
| 186 | + |
| 187 | + ``` shell copy |
| 188 | + ~/gen-jwt.sh $AUTOMATION_APP_CLIENT_ID ~/octocat-automation-app.2025-10-08.private-key.pem |
| 189 | + ``` |
| 190 | + |
| 191 | +1. Copy the JWT (the long string after `JWT= `) and save it as a variable. |
| 192 | + |
| 193 | + ``` shell copy |
| 194 | + export AUTO_JWT='abcde12345' |
| 195 | + ``` |
| 196 | + |
| 197 | +1. Use the JWT to authenticate a request for an installation access token, this time passing the installation ID and JWT for the newly installed automation app. |
| 198 | + |
| 199 | + ```shell copy |
| 200 | + gh api --method POST "/app/installations/$AUTOMATION_APP_INSTALL_ID/access_tokens" --header "Authorization: Bearer $AUTO_JWT" |
| 201 | + ``` |
| 202 | + |
| 203 | +1. Copy the new installation access token and save it as the `AUTOMATION_APP_INSTALL_TOKEN` variable. |
| 204 | + |
| 205 | + ``` shell copy |
| 206 | + export AUTOMATION_APP_INSTALL_TOKEN='abcde12345' |
| 207 | + ``` |
| 208 | + |
| 209 | +## 6. Automate a process |
| 210 | + |
| 211 | +The installation token you just received gives you permission to create a repository in the organization where the app is installed. |
| 212 | + |
| 213 | +1. Run the following command. Notice we're authenticating with the installation token we just obtained. |
| 214 | +
|
| 215 | + ```shell copy |
| 216 | + gh api --method POST \ |
| 217 | + "/orgs/$ORG/repos" \ |
| 218 | + --header "Authorization: Bearer $AUTOMATION_APP_INSTALL_TOKEN" \ |
| 219 | + --header "Accept: application/vnd.github+json" \ |
| 220 | + --header "X-GitHub-Api-Version: 2022-11-28" \ |
| 221 | + --field "name=automatic-repo" \ |
| 222 | + --field "description=Repository created automatically using GitHub App automation" \ |
| 223 | + --field "private=false" \ |
| 224 | + --field "auto_init=true" |
| 225 | + ``` |
| 226 | +
|
| 227 | +1. To check the repository was created successfully, go to `https://github.com/orgs/ORG/repositories`, replacing ORG with the name of your organization. |
| 228 | +
|
| 229 | +{% note %} |
| 230 | +
|
| 231 | +Was the repository created successfully? |
| 232 | +
|
| 233 | +<a href="https://docs.github.io/success-test/yes.html" target="_blank" class="btn btn-outline mt-3 mr-3 no-underline"><span>Yes</span></a> <a href="https://docs.github.io/success-test/no.html" target="_blank" class="btn btn-outline mt-3 mr-3 no-underline"><span>No</span></a> |
| 234 | +
|
| 235 | +{% endnote %} |
| 236 | +
|
| 237 | +## 7. Uninstall the apps |
| 238 | +
|
| 239 | +For security, uninstall the apps from the enterprise and organization. This will revoke all tokens associated with the apps. For instructions, see [AUTOTITLE](/apps/using-github-apps/reviewing-and-modifying-installed-github-apps). |
| 240 | +
|
| 241 | +## Next steps |
| 242 | +
|
| 243 | +You have seen how to install an app programmatically in organizations and run an automation. Now, you should be ready to automate a real process across multiple organizations. For more information about what apps can do, see [AUTOTITLE](/apps/creating-github-apps/about-creating-github-apps/about-creating-github-apps). |
| 244 | +
|
| 245 | +>[!TIP] In the real world, you would likely perform the installation as a one-time process. The organization-level automation would be defined in a separate script, triggered by webhooks or cron jobs. However, security-conscious enterprises may prefer to install and uninstall an app every time the automation runs, in order to limit impact if the app's private key is exposed. |
0 commit comments