terraform-provider-github
terraform-provider-github copied to clipboard
[Feature Request] Manage GitHub Apps via Terraform
Hey Team,
I was looking for a way to manage GitHub apps [1] via Terraform but quickly realized there is none. It would be great to be able to setup a GitHub app, here are some exemplary settings one might want to manage:
- app name
- permissions
- webhook URL and secret
- event subscriptions
Cheers!
Terraform Version
Terraform v0.12.28
References
[1] https://developer.github.com/apps/about-apps/
It would be great to be able to setup a GitHub app
I've been playing around with this a little bit, and the public API support for GitHub Apps isn't where it needs to be in order to support standard CRUD operations on a GitHub App. I've spoken with the team internally and they're aware of the issue but it isn't an immediate priority.
The GraphQL API doesn't support App operations at the moment.
The auth used for creating Apps on the go-github
package uses screen-scraping, which requires a One Time Password and which might rule out using it as part of the Terraform provider; I have some reservations about the stability of such a package.
In any event, I wanted to create a proof of concept, so I created a sample app with hard-coded values but I haven't been able to successfully get an App created. When skipping the OTP, I get a 406 Not Acceptable
each time, and when attempting to provide a OTP through my standard 2FA app, the client panics when authenticating.
There might be something I'm missing on how the screen-scraping package of the google/go-github
module is supposed to work. If there is, I'd love to hear about it!
In the meantime, we'll look into what operations we can support given an existing App.
I've investigated a little bit more on how to programmatically support the requested GitHub Apps operations.
- app name
I can't find anything in the API documentation about changing the name. I don't think the API that works behind the UI is public.
- permissions
Can be managed when creating an installation, but updating doesn't appear to be supported in the public API either.
- webhook URL and secret
REST docs are here, but that functionality doesn't appear to be available in go-github
yet.
- event subscriptions
I believe these are managed through the same API as permissions above.
It definitely feels like we're working without tools on this feature a bit.
Thanks for scoping this out and reporting back to the rest of us on this. Definitely feels like the wrong time to be implementing automation around GitHub App creation as support for this via api.github.com
is limited with no enhancements planned in the near-term.
My suggestion is we park this for now given the complexity of the creation path. However, we could pave paths for import-and-manage style workflows if there is more feedback from the community that this would be used.
This would definitely be of huge value. We are heading towards of managing dozens of GitHub Apps in our organization. For transparency reasons and documentation it would be great to have them as IaC.
Since github_app_installation_repository
is already implemented, this would simply be awesome.
I'm really craving for this feature...
We manage tens of github apps across 3 orgs (github enterprise) in our company. Changing everything manually and keeping in sync is PITA. E.g. recently I have to change "subscribe to events" in all apps, so I planned to write a tf module to manage our github app and automate that, but I was shocked there is no terraform support for it apparently :(
I've asked around internally and linked this issue along to the team. I know there's a lot of interest in this recorded. The team is looking at it but is not working on it presently and I can't offer a timeframe now, unfortunately.
Thank you for that @kfcampbell , there's definitely keen interest!
Also, and I'm not sure if this is the right issue to ask for it, it'd be nice to have a fine grained token that'd allow to install one or more specific apps, while not allowing to install other apps, in some or all org repos. Having a GitOps operation where there's an admin PAT running code is dangerous and right now we are forced to run the tf apply
manually on our own terminal, that's no good.
+1 this would be a lovely feature. Is there any update on if this might be explored?
I agree that this would be an excellent feature. I've seen some more people asking for it internally, which is good, but no idea on a timeframe for implementation/release yet unfortunately.
+1 here. We also recently started to use github_enterprise_organization
but now we miss managing app for those organization
+1 here. At my day job at $very_large_corp we could really use this. At the moment it introduces a manual step in an otherwise largely automated process.
+1 here
My team has to be able to add/remove thousands, yes, thousands of repositories from a given GitHub Application "Repository Access", having to manually perform this task is madness.
Terraform Integrations/github provider does support github_app_installation_repositories
but that is all about it.
Does anybody know any workaround for now?? API calls or anything that can be somewhat automated?? This is a 2020 request and so far it looks like nothing was really done :(
Thanks
+1 here
Spend a couple of hours today trying to achieve a behavior that depended on accesses managed by a Github app, if the feature was available the visibility would be in plain sight so the access could be managed easily in the IaC scenario I'm working on.
it seems there is a rest api for it now https://github.com/google/go-github/blob/master/github/apps.go#L16
This part of the API has existed for some time, it can be used to manage app installations, but not apps
+1 Registering interest in this one also - same as above it would be nice to fully automate this. Does anybody know where we can register interest in the required API change with github?
@phill-lewis it would be worth filing a request here.
Come on GitHub, almost 4 years later and we still don't have access to this resource🥲
For what it's worth, if you're using pulumi here is a simple dynamic provider to manage GitHub App info I've been using internally for the past 6 months or so:
import * as pulumi from '@pulumi/pulumi';
import { Octokit } from '@octokit/core';
import { AppAuthentication, createAppAuth } from '@octokit/auth-app';
export interface ConfigurationResourceInputs {
appId: pulumi.Input<string>;
privateKey: pulumi.Input<string>;
clientId: pulumi.Input<string>;
clientSecret: pulumi.Input<string>;
url: pulumi.Input<string>;
secret: pulumi.Input<string>;
contentType?: pulumi.Input<string> | undefined;
}
export class Configuration extends pulumi.dynamic.Resource {
public readonly appId!: pulumi.Output<pulumi.ID>;
public readonly privateKey!: pulumi.Output<string>;
public readonly clientId!: pulumi.Output<string>;
public readonly clientSecret!: pulumi.Output<string>;
public readonly url!: pulumi.Output<string>;
public readonly secret!: pulumi.Output<string>;
public readonly contentType!: pulumi.Output<string>;
constructor(name: string, args: ConfigurationResourceInputs, opts?: pulumi.CustomResourceOptions) {
super(new ConfigurationProvider(), name, args, opts, 'github-app', 'Configuration');
}
}
interface ConfigurationInputs {
appId: string;
privateKey: string;
clientId: string;
clientSecret: string;
url: string;
secret: string;
contentType?: string | undefined;
}
export interface ConfigurationOutputs extends Omit<ConfigurationInputs, 'url' | 'secret'> {
url?: string | undefined;
secret?: string | undefined;
}
export class ConfigurationProvider implements pulumi.dynamic.ResourceProvider {
async auth({
appId,
privateKey,
clientId,
clientSecret,
}: Pick<
ConfigurationInputs,
'appId' | 'clientId' | 'clientSecret' | 'privateKey'
>): Promise<AppAuthentication> {
const auth = createAppAuth({
appId: appId,
privateKey: privateKey,
clientId: clientId,
clientSecret: clientSecret,
});
return await auth({ type: 'app' });
}
async create(inputs: ConfigurationInputs): Promise<pulumi.dynamic.CreateResult> {
const auth = await this.auth(inputs);
const octokit = new Octokit({ auth: auth.token });
try {
const { data } = await octokit.request('PATCH /app/hook/config', {
data: JSON.stringify({
url: inputs.url,
secret: inputs.secret,
insecure_ssl: '0',
content_type: inputs.contentType || 'json',
}),
});
return {
id: inputs.appId,
outs: {
appId: inputs.appId,
privateKey: inputs.privateKey,
clientId: inputs.clientId,
clientSecret: inputs.clientSecret,
url: data.url,
secret: data.secret,
contentType: data.content_type,
},
};
} catch (err) {
await pulumi.log.error(`${JSON.stringify(err, null, 2)}`);
throw new Error('Failed to set github app webhook config');
}
}
async diff(
_id: pulumi.ID,
olds: ConfigurationOutputs,
news: ConfigurationInputs,
): Promise<pulumi.dynamic.DiffResult> {
let changes: boolean = false;
let replaces: string[] = [];
let keys = Object.keys(news) as Array<Extract<keyof typeof news, string>>;
for (const key of keys) {
if (JSON.stringify(olds[key]) !== JSON.stringify(news[key])) {
changes = true;
if (key === 'appId' || key === 'privateKey' || key === 'clientId' || key === 'clientSecret') {
replaces.push(key);
}
}
}
return { changes, replaces };
}
async update(
id: pulumi.ID,
_olds: ConfigurationOutputs,
news: ConfigurationInputs,
): Promise<pulumi.dynamic.UpdateResult> {
const auth = await this.auth(news);
const octokit = new Octokit({ auth: auth.token });
try {
const { data } = await octokit.request('PATCH /app/hook/config', {
data: JSON.stringify({
url: news.url,
secret: news.secret,
insecure_ssl: '0',
content_type: news.contentType || 'json',
}),
});
return {
outs: {
appId: id,
privateKey: news.privateKey,
clientId: news.clientId,
clientSecret: news.clientSecret,
url: data.url,
secret: data.secret,
contentType: data.content_type,
},
};
} catch (err) {
await pulumi.log.error(`${JSON.stringify(err, null, 2)}`);
throw new Error('Failed to set github app webhook config');
}
}
async read(id: pulumi.ID, props?: ConfigurationOutputs): Promise<pulumi.dynamic.ReadResult> {
if (!props) {
throw new Error('Props undefined');
}
const auth = await this.auth({ ...props, appId: id });
const octokit = new Octokit({ auth: auth.token });
try {
const { data } = await octokit.request('GET /app/hook/config');
return {
id,
props: {
appId: id,
privateKey: props.privateKey,
clientId: props.clientId,
clientSecret: props.clientSecret,
url: data.url,
secret: data.secret,
contentType: data.content_type,
},
};
} catch (err) {
await pulumi.log.error(`${JSON.stringify(err, null, 2)}`);
throw new Error('Failed to set github app webhook config');
}
}
}
Just in case that would help others who were trying to figure out how to deal with that situation. Pulumi's GitHub provider is generated based on the terraform provider sources, so faces the same problem documented in this thread.
+1. I would love to contribute as well.