Skip to content

Instantly share code, notes, and snippets.

@GTRekter
Created April 15, 2023 06:58
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save GTRekter/890746911dfd87d0719abce9f6175761 to your computer and use it in GitHub Desktop.
Save GTRekter/890746911dfd87d0719abce9f6175761 to your computer and use it in GitHub Desktop.
This is a bash script that configures several organization policies for a specified Azure DevOps organization. The script makes use of the Azure DevOps REST API to perform PATCH requests on the different policies. The policies that are being configured include disallowing third-party application access via OAuth, disallowing SSH authentication, …
PAT=""
ORG_NAME=""
DEFAULT_JSON={
"organization": {
"policies": {
"disallow_third_party_application_access_via_oauth": false,
"disallow_ssh_authentication": false,
"log_audit_events": true,
"allow_public_projects": true,
"additional_protections_public_package_registries": true,
"enable_azure_active_directory_conditional_access_policy_validation": true,
"disallow_external_guest_access": false,
"allow_team_and_project_administrators_to_invite_new_users": true,
"request_access": {
"enable": false,
"url": "https://dev.azure.com/GTRekter"
}
}
}
}
echo "Configure $ORG_NAME organization policies"
THIRD_PARTY_ACCESS_VIA_OAUTH=$(echo "$DEFAULT_JSON" | jq -r '.organization.policies.disallow_third_party_application_access_via_oauth')
echo "Setting Third-party application access via OAuth to $THIRD_PARTY_ACCESS_VIA_OAUTH"
RESPONSE=$(curl --silent \
--request PATCH \
--write-echo "\n%{http_code}" \
--header "Authorization: Basic $(echo -n :$PAT | base64)" \
--header "Content-Type: application/json-patch+json" \
--data-raw '[{"from":"","op":2,"path":"/Value","value":"'$THIRD_PARTY_ACCESS_VIA_OAUTH'"}]' \
"https://dev.azure.com/$ORG_NAME/_apis/OrganizationPolicy/Policies/Policy.DisallowOAuthAuthentication?api-version=5.0-preview.1")
HTTP_STATUS=$(tail -n1 <<< "$RESPONSE")
if [ $HTTP_STATUS != 204 ]; then
echo "Error during the configuration of the Third-party application access via OAuth policy. $RESPONSE_BODY"
exit 1;
else
echo "Configuration of the Third-party application access via OAuth policy was successful"
fi
SSH_AUTHENTICATION=$(echo "$DEFAULT_JSON" | jq -r '.organization.policies.disallow_ssh_authentication')
echo "Setting SSH authentication to $SSH_AUTHENTICATION"
RESPONSE=$(curl --silent \
--request PATCH \
--write-echo "\n%{http_code}" \
--header "Authorization: Basic $(echo -n :$PAT | base64)" \
--header "Content-Type: application/json-patch+json" \
--data-raw '[{"from":"","op":2,"path":"/Value","value":"'$SSH_AUTHENTICATION'"}]' \
"https://dev.azure.com/$ORG_NAME/_apis/OrganizationPolicy/Policies/Policy.DisallowSecureShell?api-version=5.0-preview.1")
HTTP_STATUS=$(tail -n1 <<< "$RESPONSE")
if [ $HTTP_STATUS != 204 ]; then
echo "Error during the configuration of the SSH authentication policy. $RESPONSE_BODY"
exit 1;
else
echo "Configuration of the SSH authentication policy was successful"
fi
LOG_AUDIT_EVENTS=$(echo "$DEFAULT_JSON" | jq -r '.organization.policies.log_audit_events')
echo "Setting Log audit events to $LOG_AUDIT_EVENTS"
RESPONSE=$(curl --silent \
--request PATCH \
--write-echo "\n%{http_code}" \
--header "Authorization: Basic $(echo -n :$PAT | base64)" \
--header "Content-Type: application/json-patch+json" \
--data-raw '[{"from":"","op":2,"path":"/Value","value":"'$LOG_AUDIT_EVENTS'"}]' \
"https://dev.azure.com/$ORG_NAME/_apis/OrganizationPolicy/Policies/Policy.LogAuditEvents?api-version=5.0-preview.1")
HTTP_STATUS=$(tail -n1 <<< "$RESPONSE")
if [ $HTTP_STATUS != 204 ]; then
echo "Error during the configuration of the Log audit events policy. $RESPONSE_BODY"
exit 1;
else
echo "Configuration of the Log audit events policy was successful"
fi
ALLOW_PUBLIC_PROJECTS=$(echo "$DEFAULT_JSON" | jq -r '.organization.policies.allow_public_projects')
echo "Setting Allow public projects to $ALLOW_PUBLIC_PROJECTS"
RESPONSE=$(curl --silent \
--request PATCH \
--write-echo "\n%{http_code}" \
--header "Authorization: Basic $(echo -n :$PAT | base64)" \
--header "Content-Type: application/json-patch+json" \
--data-raw '[{"from":"","op":2,"path":"/Value","value":"'$ALLOW_PUBLIC_PROJECTS'"}]' \
"https://dev.azure.com/$ORG_NAME/_apis/OrganizationPolicy/Policies/Policy.AllowAnonymousAccess?api-version=5.0-preview.1")
HTTP_STATUS=$(tail -n1 <<< "$RESPONSE")
if [ $HTTP_STATUS != 204 ]; then
echo "Error during the configuration of the Allow public projects policy. $RESPONSE_BODY"
exit 1;
else
echo "Configuration of the Allow public projects policy was successful"
fi
ARTIFACTS_EXTERNAL_PACKAGE_PROTECTION_TOKEN=$(echo "$DEFAULT_JSON" | jq -r '.organization.policies.additional_protections_public_package_registries')
echo "Setting Additional protections for public package registries to $ARTIFACTS_EXTERNAL_PACKAGE_PROTECTION_TOKEN"
RESPONSE=$(curl --silent \
--request PATCH \
--write-echo "\n%{http_code}" \
--header "Authorization: Basic $(echo -n :$PAT | base64)" \
--header "Content-Type: application/json-patch+json" \
--data-raw '[{"from":"","op":2,"path":"/Value","value":"'$ARTIFACTS_EXTERNAL_PACKAGE_PROTECTION_TOKEN'"}]' \
"https://dev.azure.com/$ORG_NAME/_apis/OrganizationPolicy/Policies/Policy.ArtifactsExternalPackageProtectionToken?api-version=5.0-preview.1")
HTTP_STATUS=$(tail -n1 <<< "$RESPONSE")
if [ $HTTP_STATUS != 204 ]; then
echo "Error during the configuration of the Additional protections for public package registries policy. $RESPONSE_BODY"
exit 1;
else
echo "Configuration of the Additional protections for public package registries policy was successful"
fi
ENFORCE_AZURE_ACTIVE_DIRECTORY_CONDITIONAL_ACCESS=$(echo "$DEFAULT_JSON" | jq -r '.organization.policies.enable_azure_active_directory_conditional_access_policy_validation')
echo "Setting Additional protections for public package registries to $ENFORCE_AZURE_ACTIVE_DIRECTORY_CONDITIONAL_ACCESS"
RESPONSE=$(curl --silent \
--request PATCH \
--write-echo "\n%{http_code}" \
--header "Authorization: Basic $(echo -n :$PAT | base64)" \
--header "Content-Type: application/json-patch+json" \
--data-raw '[{"from":"","op":2,"path":"/Value","value":"'$ENFORCE_AZURE_ACTIVE_DIRECTORY_CONDITIONAL_ACCESS'"}]' \
"https://dev.azure.com/$ORG_NAME/_apis/OrganizationPolicy/Policies/Policy.EnforceAADConditionalAccess?api-version=5.0-preview.1")
HTTP_STATUS=$(tail -n1 <<< "$RESPONSE")
if [ $HTTP_STATUS != 204 ]; then
echo "Error during the configuration of the Additional protections for public package registries policy. $RESPONSE_BODY"
exit 1;
else
echo "Configuration of the Additional protections for public package registries policy was successful"
fi
ALLOW_TEAM_ADMINS_INVITATIONS_ACCESS_TOKEN=$(echo "$DEFAULT_JSON" | jq -r '.organization.policies.allow_team_and_project_administrators_to_invite_new_users')
echo "Setting Additional protections for public package registries to $ALLOW_TEAM_ADMINS_INVITATIONS_ACCESS_TOKEN"
RESPONSE=$(curl --silent \
--request PATCH \
--write-echo "\n%{http_code}" \
--header "Authorization: Basic $(echo -n :$PAT | base64)" \
--header "Content-Type: application/json-patch+json" \
--data-raw '[{"from":"","op":2,"path":"/Value","value":"'$ALLOW_TEAM_ADMINS_INVITATIONS_ACCESS_TOKEN'"}]' \
"https://dev.azure.com/$ORG_NAME/_apis/OrganizationPolicy/Policies/Policy.AllowTeamAdminsInvitationsAccessToken?api-version=5.0-preview.1")
HTTP_STATUS=$(tail -n1 <<< "$RESPONSE")
if [ $HTTP_STATUS != 204 ]; then
echo "Error during the configuration of the Additional protections for public package registries policy. $RESPONSE_BODY"
exit 1;
else
echo "Configuration of the Additional protections for public package registries policy was successful"
fi
ALLOW_GUEST_USERS=$(echo "$DEFAULT_JSON" | jq -r '.organization.policies.disallow_external_guest_access')
echo "Setting Allow guest users to $ALLOW_GUEST_USERS"
RESPONSE=$(curl --silent \
--request PATCH \
--write-echo "\n%{http_code}" \
--header "Authorization: Basic $(echo -n :$PAT | base64)" \
--header "Content-Type: application/json-patch+json" \
--data-raw '[{"from":"","op":2,"path":"/Value","value":"'$ALLOW_GUEST_USERS'"}]' \
"https://dev.azure.com/$ORG_NAME/_apis/OrganizationPolicy/Policies/Policy.DisallowAadGuestUserAccess?api-version=5.0-preview.1")
HTTP_STATUS=$(tail -n1 <<< "$RESPONSE")
if [ $HTTP_STATUS != 204 ]; then
echo "Error during the configuration of the Allow guest users policy. $RESPONSE_BODY"
exit 1;
else
echo "Configuration of the Allow guest users policy was successful"
fi
REQUEST_ACCESS=$(echo "$DEFAULT_JSON" | jq -r '.organization.policies.request_access.enable')
REQUEST_ACCESS_URL=$(echo "$DEFAULT_JSON" | jq -r '.organization.policies.request_access.url')
if [ ! $REQUEST_ACCESS ]; then
echo "Read organization ID. This property is needed to get a list of service endpoints"
RESPONSE=$(curl --silent \
--write-echo "\n%{http_code}" \
--header "Authorization: Basic $(echo -n :$PAT | base64)" \
--header "Content-Type: application/json" \
--data-raw '{"contributionIds": ["ms.vss-features.my-organizations-data-provider"],"dataProviderContext":{"properties":{}}}' \
"https://dev.azure.com/$ORG_NAME/_apis/Contribution/HierarchyQuery?api-version=5.0-preview.1")
HTTP_STATUS=$(tail -n1 <<< "$RESPONSE")
RESPONSE_BODY=$(sed '$ d' <<< "$RESPONSE")
if [ $HTTP_STATUS != 200 ]; then
echo "Failed to get the list of existing service endpoints. $RESPONSE"
exit 1;
else
echo "The list of existing service endpoints was succesfully retrieved"
fi
ORG_ID=$(echo "$RESPONSE_BODY" | jq '.dataProviders."ms.vss-features.my-organizations-data-provider".organizations[] | select(.name == "'"$ORG_NAME"'") | .id' | tr -d '"')
echo "Setting $ORG_NAME organization url to $REQUEST_ACCESS_URL"
RESPONSE=$(curl --silent \
--request PATCH \
--write-echo "\n%{http_code}" \
--header "Authorization: Basic $(echo -n :$PAT | base64)" \
--header "Content-Type: application/json-patch+json" \
--data-raw '[{"from":"","op":2,"path":"/Value","value":"'$ALLOW_GUEST_USERS'"}]' \
"https://vssps.dev.azure.com/$ORG_NAME/_apis/Organization/Collections/$ORG_ID/Properties?api-version=5.0-preview.1")
HTTP_STATUS=$(tail -n1 <<< "$RESPONSE")
if [ $HTTP_STATUS != 200 ]; then
echo "Error during the configuration of the organization url. $RESPONSE_BODY"
exit 1;
else
echo "Configuration of the organization url was successful"
fi
fi
echo "Setting Request access to $REQUEST_ACCESS"
RESPONSE=$(curl --silent \
--request PATCH \
--write-echo "\n%{http_code}" \
--header "Authorization: Basic $(echo -n :$PAT | base64)" \
--header "Content-Type: application/json-patch+json" \
--data-raw '[{"from":"","op":2,"path":"/Value","value":"'$REQUEST_ACCESS'"}]' \
"https://dev.azure.com/$ORG_NAME/_apis/OrganizationPolicy/Policies/Policy.AllowRequestAccessToken?api-version=5.0-preview.1")
HTTP_STATUS=$(tail -n1 <<< "$RESPONSE")
if [ $HTTP_STATUS != 204 ]; then
echo "Error during the configuration of the Request access policy. $RESPONSE_BODY"
exit 1;
else
echo "Configuration of the Request access policy was successful"
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment