Skip to content

Commit cfdead4

Browse files
committed
feat: add new organization reconnaissance scripts
1 parent d35148f commit cfdead4

7 files changed

+360
-3
lines changed

gh-cli/README.md

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -791,22 +791,48 @@ Gets a team
791791

792792
### get-organization-webhooks.sh
793793

794-
Gets a list of webhooks in an organization
794+
Gets a list of webhooks (and webhook information) in an organization
795795

796796
> [!NOTE]
797797
> Requires a GitHub PAT instead of using the OAuth token with the `gh api` command - the OAuth token can only retrieve webhooks it created
798798
799+
### get-organizations-apps-count.sh
800+
801+
Gets the count of apps in all organizations in a given enterprise
802+
803+
### get-organizations-apps.sh
804+
805+
Gets a list of apps (and app information) in all organizations in a given enterprise
806+
807+
### get-organizations-codeowner-usage.sh
808+
809+
Gets the usage of CODEOWNERS files in all repositories in all organizations in a given enterprise (checks `HEAD` for `./`, `./.github`, and `./docs` and returns `TRUE` or `FALSE` for each repository)
810+
811+
### get-organizations-discussions-count.sh
812+
813+
Gets the usage of discussions in all repositories in all organizations in a given enterprise (org-wide discussions have to be created in a repository, so this covers that as well)
814+
815+
816+
799817
### get-organizations-for-user.sh
800818

801819
Gets the list of organizations a user is a member of. This only returns organizations accessible to the person running the script, i.e.: organizations they are also a member of, or public organizations
802820

821+
### get-organizations-projects-count-classic.sh
822+
823+
Gets the count of organization projects (classic projects) in all organizations in a given enterprise
824+
803825
### get-organizations-projects-count.sh
804826

805827
Gets the count of projects (ProjectsV2) in all organizations in a given enterprise
806828

807-
### get-organizations-webhooks-in-enterprise.sh
829+
### get-organizations-settings.sh
830+
831+
Gets the settings for all organizations in an enterprise
832+
833+
### get-organizations-webhooks.sh
808834

809-
Gets a list of webhooks in all organizations in an enterprise
835+
Gets a list of webhooks (and webhook information) in all organizations in an enterprise
810836

811837
> [!NOTE]
812838
> Requires a GitHub PAT instead of using the OAuth token with the `gh api` - the OAuth token can only retrieve webhooks it created
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
#!/bin/bash
2+
3+
# gets the settings for all organizations in an enterprise
4+
5+
# need: `gh auth refresh -h github.com -s read:org -s read:enterprise`
6+
7+
# note: tsv is the default format
8+
# tsv is a subset of fields, json is all fields
9+
10+
if [ $# -lt 1 ]
11+
then
12+
echo "usage: $0 <enterprise-slug> <hostname> > output.tsv"
13+
exit 1
14+
fi
15+
16+
export PAGER=""
17+
enterpriseslug=$1
18+
hostname=$2
19+
20+
# set hostname to github.com by default
21+
if [ -z "$hostname" ]
22+
then
23+
hostname="github.com"
24+
fi
25+
26+
organizations=$(gh api graphql --paginate --hostname $hostname -f enterpriseName="$enterpriseslug" -f query='
27+
query getEnterpriseOrganizations($enterpriseName: String! $endCursor: String) {
28+
enterprise(slug: $enterpriseName) {
29+
organizations(first: 100, after: $endCursor) {
30+
nodes {
31+
id
32+
login
33+
}
34+
pageInfo {
35+
endCursor
36+
hasNextPage
37+
}
38+
}
39+
}
40+
}' --jq '.data.enterprise.organizations.nodes[].login')
41+
42+
echo -e "Org\tApp Count"
43+
44+
for org in $organizations
45+
do
46+
gh api "orgs/$org/installations" --hostname $hostname --jq ". | [\"$org\", .total_count] | @tsv"
47+
done

gh-cli/get-organizations-apps.sh

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
#!/bin/bash
2+
3+
# gets the settings for all organizations in an enterprise
4+
5+
# need: `gh auth refresh -h github.com -s read:org -s read:enterprise`
6+
7+
# note: tsv is the default format
8+
# tsv is a subset of fields, json is all fields
9+
10+
if [ $# -lt 1 ]
11+
then
12+
echo "usage: $0 <enterprise-slug> <hostname> <format: tsv|json> > output.tsv"
13+
exit 1
14+
fi
15+
16+
enterpriseslug=$1
17+
hostname=$2
18+
format=$3
19+
export PAGER=""
20+
21+
# set hostname to github.com by default
22+
if [ -z "$hostname" ]
23+
then
24+
hostname="github.com"
25+
fi
26+
27+
if [ -z "$format" ]
28+
then
29+
format="tsv"
30+
fi
31+
32+
organizations=$(gh api graphql --paginate --hostname $hostname -f enterpriseName="$enterpriseslug" -f query='
33+
query getEnterpriseOrganizations($enterpriseName: String! $endCursor: String) {
34+
enterprise(slug: $enterpriseName) {
35+
organizations(first: 100, after: $endCursor) {
36+
nodes {
37+
id
38+
login
39+
}
40+
pageInfo {
41+
endCursor
42+
hasNextPage
43+
}
44+
}
45+
}
46+
}' --jq '.data.enterprise.organizations.nodes[].login')
47+
48+
if [ "$format" == "tsv" ]; then
49+
echo -e "Org\tApp Slug\tApp ID\tCreated At\tUpdated At\tPermissions\tEvents"
50+
fi
51+
52+
for org in $organizations
53+
do
54+
if [ "$format" == "tsv" ]; then
55+
gh api "orgs/$org/installations" --hostname $hostname --jq ".installations[] | [\"$org\", .app_slug, .app_id, .created_at, .updated_at, (.permissions | join(\",\")), (if .events | length == 0 then \"null\" else .events | join(\",\") end)] | @tsv"
56+
else
57+
gh api "orgs/$org/installations" --hostname $hostname --jq '.installations[]'
58+
fi
59+
done
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
#!/bin/bash
2+
3+
# gets the discussions count for all organizations in an enterprise
4+
5+
# need: `gh auth refresh -h github.com -s read:org -s read:enterprise`
6+
7+
if [ $# -lt 1 ]; then
8+
echo "usage: $0 <enterprise slug> <hostname> > output.tsv"
9+
exit 1
10+
fi
11+
12+
enterprise=$1
13+
hostname=$2
14+
export PAGER=""
15+
16+
# set hostname to github.com by default
17+
if [ -z "$hostname" ]
18+
then
19+
hostname="github.com"
20+
fi
21+
22+
echo -e "Repository\tUses Codeowners"
23+
24+
# we can't do everything in a single call b/c we need to paginate orgs and then paginate repos in the next query (can't do double pagination with gh api)
25+
organizations=$(gh api graphql --paginate --hostname $hostname -f enterpriseName="$enterprise" -f query='
26+
query getEnterpriseOrganizations($enterpriseName: String! $endCursor: String) {
27+
enterprise(slug: $enterpriseName) {
28+
organizations(first: 100, after: $endCursor) {
29+
nodes {
30+
id
31+
login
32+
}
33+
pageInfo {
34+
endCursor
35+
hasNextPage
36+
}
37+
}
38+
}
39+
}' --jq '.data.enterprise.organizations.nodes[].login')
40+
41+
for org in $organizations
42+
do
43+
gh api graphql --paginate --hostname $hostname -f orgName="$org" -f query='
44+
query getOrganizationRepositories($orgName: String! $endCursor: String) {
45+
organization(login: $orgName) {
46+
repositories(first: 100, after: $endCursor) {
47+
nodes {
48+
nameWithOwner
49+
root: object(expression: "HEAD:CODEOWNERS") {
50+
... on Blob {
51+
text
52+
}
53+
}
54+
github: object(expression: "HEAD:.github/CODEOWNERS") {
55+
... on Blob {
56+
text
57+
}
58+
}
59+
docs: object(expression: "HEAD:docs/CODEOWNERS") {
60+
... on Blob {
61+
text
62+
}
63+
}
64+
}
65+
pageInfo {
66+
endCursor
67+
hasNextPage
68+
}
69+
}
70+
}
71+
}' --jq '.data.organization.repositories.nodes[] | {nameWithOwner: .nameWithOwner, hasCodeowners: if .root.text or .github.text or .docs.text then "TRUE" else "FALSE" end} | [.nameWithOwner, .hasCodeowners] | @tsv'
72+
done
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
#!/bin/bash
2+
3+
# gets the discussions count for all organizations in an enterprise
4+
5+
# need: `gh auth refresh -h github.com -s read:org -s read:enterprise`
6+
7+
if [ $# -lt 1 ]; then
8+
echo "usage: $0 <enterprise slug> <hostname> > output.tsv"
9+
exit 1
10+
fi
11+
12+
enterprise=$1
13+
hostname=$2
14+
export PAGER=""
15+
16+
# set hostname to github.com by default
17+
if [ -z "$hostname" ]
18+
then
19+
hostname="github.com"
20+
fi
21+
22+
echo -e "Repository\tDiscussion Count"
23+
24+
# we can't do everything in a single call b/c we need to paginate orgs and then paginate repos in the next query (can't do double pagination with gh api)
25+
organizations=$(gh api graphql --paginate --hostname $hostname -f enterpriseName="$enterprise" -f query='
26+
query getEnterpriseOrganizations($enterpriseName: String! $endCursor: String) {
27+
enterprise(slug: $enterpriseName) {
28+
organizations(first: 100, after: $endCursor) {
29+
nodes {
30+
id
31+
login
32+
}
33+
pageInfo {
34+
endCursor
35+
hasNextPage
36+
}
37+
}
38+
}
39+
}' --jq '.data.enterprise.organizations.nodes[].login')
40+
41+
for org in $organizations
42+
do
43+
gh api graphql --paginate --hostname $hostname -f orgName="$org" -f query='
44+
query getOrganizationRepositories($orgName: String! $endCursor: String) {
45+
organization(login: $orgName) {
46+
repositories(first: 100, after: $endCursor) {
47+
nodes {
48+
nameWithOwner
49+
discussions {
50+
totalCount
51+
}
52+
}
53+
pageInfo {
54+
endCursor
55+
hasNextPage
56+
}
57+
}
58+
}
59+
}' --jq '.data.organization.repositories.nodes[] | [.nameWithOwner, .discussions.totalCount] | @tsv'
60+
done
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#!/bin/bash
2+
3+
# gets the projects count (classic) for all organizations in an enterprise
4+
5+
# need: `gh auth refresh -h github.com -s read:org -s read:enterprise`
6+
7+
if [ $# -lt 1 ]; then
8+
echo "usage: $0 <enterprise slug> <hostname> > output.tsv"
9+
exit 1
10+
fi
11+
12+
enterprise=$1
13+
hostname=$2
14+
export PAGER=""
15+
16+
# set hostname to github.com by default
17+
if [ -z "$hostname" ]
18+
then
19+
hostname="github.com"
20+
fi
21+
22+
echo -e "Organization\tProjects Count (classic)"
23+
24+
gh api graphql -f enterprise="$enterprise" --paginate --hostname $hostname -f query='query($enterprise:String!, $endCursor: String) {
25+
enterprise(slug:$enterprise) {
26+
organizations(first:100, after: $endCursor) {
27+
pageInfo { hasNextPage endCursor }
28+
nodes {
29+
name
30+
projects { totalCount }
31+
}
32+
}
33+
}
34+
}' --jq '.data.enterprise.organizations.nodes[] | [.name, .projects.totalCount] | @tsv'
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
#!/bin/bash
2+
3+
# gets the settings for all organizations in an enterprise
4+
5+
# need: `gh auth refresh -h github.com -s read:org -s read:enterprise`
6+
7+
# note: tsv is the default format
8+
# tsv is a subset of fields, json is all fields
9+
10+
if [ $# -lt 1 ]
11+
then
12+
echo "usage: $0 <enterprise slug> <hostname> <format: tsv|json> > output.tsv/json"
13+
exit 1
14+
fi
15+
16+
enterpriseslug=$1
17+
hostname=$2
18+
format=$3
19+
export PAGER=""
20+
21+
# set hostname to github.com by default
22+
if [ -z "$hostname" ]
23+
then
24+
hostname="github.com"
25+
fi
26+
27+
if [ -z "$format" ]
28+
then
29+
format="tsv"
30+
fi
31+
32+
organizations=$(gh api graphql --hostname $hostname --paginate -f enterpriseName="$enterpriseslug" -f query='
33+
query getEnterpriseOrganizations($enterpriseName: String! $endCursor: String) {
34+
enterprise(slug: $enterpriseName) {
35+
organizations(first: 100, after: $endCursor) {
36+
nodes {
37+
id
38+
login
39+
}
40+
pageInfo {
41+
endCursor
42+
hasNextPage
43+
}
44+
}
45+
}
46+
}' --jq '.data.enterprise.organizations.nodes[].login')
47+
48+
if [ "$format" == "tsv" ]; then
49+
echo -e "Org Login\tOrg Name\tOrg Desc\tDefault Repo Permission\tMembers Can Create Repos\t\tMembers Allowed Repos Creation Type\tMembers Can Create Public Repos\tMembers Can Create Private Repos\tMembers Can Create Internal Repos\tMembers Can Fork Private Repos"
50+
fi
51+
52+
for org in $organizations
53+
do
54+
if [ "$format" == "tsv" ]; then
55+
gh api "orgs/$org" --hostname $hostname --jq ". | [\"$org\", .name, .description, .default_repository_permission, .members_can_create_repositories, .members_allowed_repository_creation_type, .members_can_create_public_repositories, .members_can_create_private_repositories, .members_can_create_internal_repositories, .members_can_fork_private_repositories] | @tsv"
56+
else
57+
gh api "orgs/$org" --hostname $hostname
58+
fi
59+
done

0 commit comments

Comments
 (0)