Skip to content
This repository has been archived by the owner on Dec 13, 2022. It is now read-only.

Work on improving the create service principal generator #90

Merged
merged 2 commits into from
Dec 6, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/brave-olives-reply.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@arkahna/nx-terraform': minor
---

Improvements to service principal creation
16 changes: 15 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,19 @@
],
"[terraform]": {
"editor.defaultFormatter": "hashicorp.terraform"
}
},
"hediet.vscode-drawio.theme": "Kennedy",
"hediet.vscode-drawio.customFonts": [
"Calibri"
],
"hediet.vscode-drawio.defaultVertexStyle": {
"fontFamily": "Calibri",
"fillColor": "#dae8fc",
"strokeColor": "#6c8ebf",
"strokeWidth": "1",
"labelBackgroundColor": "none"
},
"hediet.vscode-drawio.defaultEdgeStyle": {
"edgeStyle": "orthogonalEdgeStyle;"
},
}
26 changes: 26 additions & 0 deletions libs/nx-terraform/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,30 @@ pnpm nx g \
@arkahna/nx-terraform:add-project-environment \
<projectname> \
--environment <environmentname>
```

### create-environment-sp
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added some doco, it's now doing a fair bit


Creates a service principal for GitHub actions to use.

Need to be logged in as an application administrator role.

This generator will do the following:

- Create a service principal
- Grant the service principal the desired role on the environment resource group
- Grant the service principal permissions to write to the terraform state store (if using Azure storage for state)
- Grant the service principal permissions to read/write secrets in the environment KeyVault
- Add the Application.ReadWrite.Owner permission to the service principal
- Print the links and command line args to grant admin consent to the service principal (enabling Service Principal to Create and Maintain App Registrations)

#### Usage

```
pnpm nx g \
@arkahna/nx-terraform:create-environment-sp \
--environment <environmentname>
```

## Executors

Expand All @@ -115,3 +137,7 @@ If you are running apply multiple times locally, run with `--leaveFirewallExcept
### Lint

Needs tfsec installed, or set tfsec command to false. See https://github.com/aquasecurity/tfsec#installation

## Concepts

![Concepts](./docs/concepts.drawio.png)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think a visual around how it ends up causing the projects to be structured will be handy

Binary file added libs/nx-terraform/docs/concepts.drawio.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 0 additions & 1 deletion libs/nx-terraform/src/common/getEnvTfVars.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ export function getTfEnvVars(projectName: string, envConfig: EnvConfig, repoConf
`project_name_tag=${projectName || ''}`,
`cost_centre_tag=${repoConfig.azureCostCentre || ''}`,
`resource_prefix=${repoConfig.azureResourcePrefix || ''}`,
`github_service_principal=${envConfig.github_service_principal || ''}`,
`github_service_principal_id=${envConfig.github_service_principal_id || ''}`,
]
}
Expand Down
4 changes: 3 additions & 1 deletion libs/nx-terraform/src/common/readConfigFromEnvFile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,9 @@ export async function readConfigFromEnvFile(
environmentFile,
attributes,
environmentFileBody: body,
github_service_principal: attributes.github_service_principal,
github_service_principal_name: attributes.github_service_principal_name,
github_service_principal_id: attributes.github_service_principal_id,
github_service_principal_app_object_id: attributes.github_service_principal_app_object_id,
github_service_principal_app_client_id: attributes.github_service_principal_app_client_id,
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export default async function (
]

const idPlaceholder = 'ID_ONCE_CREATED'
const appIdPlaceholder = 'APP_ID_ONCE_CREATED'
const storageContributorRoleAssignmentArgs = [
'role',
'assignment',
Expand All @@ -64,6 +65,21 @@ export default async function (
kvScope,
]

const assignApplicationWritePermissions = [
'ad',
'app',
'permission',
'add',
'--id',
appIdPlaceholder,
'--api',
// Graph
'00000002-0000-0000-c000-000000000000',
'--api-permissions',
// Application.ReadWrite.OwnedBy
'824c81eb-e3f8-4ee6-8f6d-de7f50d565b7=Role',
]

if (isDryRun()) {
console.log('Will run:')

Expand All @@ -74,6 +90,8 @@ export default async function (
}

console.log(`> ${getEscapedCommand(`az`, keyvaultRoleAssignmentArgs)}`)

console.log(`> ${getEscapedCommand(`az`, assignApplicationWritePermissions)}`)
}

return async () => {
Expand All @@ -83,20 +101,34 @@ export default async function (
stdio: 'inherit',
})

const { stdout } = await execa(`az`, [
const { stdout: stdoutAdList } = await execa(`az`, [
'ad',
'sp',
'list',
'--display-name',
servicePrincipalName,
])
const servicePrincipalObjectId: string = JSON.parse(stdout)[0].id
const servicePrincipalObjectId: string = JSON.parse(stdoutAdList)[0].id
console.log(`Service principal id: ${servicePrincipalObjectId}`)

const { stdout: stdoutAppList } = await execa(`az`, [
'ad',
'app',
'list',
'--display-name',
servicePrincipalName,
])
const appObjectId: string = JSON.parse(stdoutAppList)[0].id
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It now gathers and outputs the AD Object ID as well as the app id and the app Object ID.

Different commands need one of these ids.

It then stores all 3 in the dev markdown.

const appClientId: string = JSON.parse(stdoutAppList)[0].appId
console.log(`Service principal app object id: ${appObjectId}`)
console.log(`Service principal app client id: ${appClientId}`)

const newAttributes: Record<string, string | undefined> = {
...environmentConfig.attributes,
github_service_principal: servicePrincipalName,
github_service_principal_name: servicePrincipalName,
github_service_principal_id: servicePrincipalObjectId,
github_service_principal_app_object_id: appObjectId,
github_service_principal_app_client_id: appClientId,
}

fs.writeFileSync(
Expand Down Expand Up @@ -124,6 +156,7 @@ ${environmentConfig.environmentFileBody}
)
}

console.log()
console.log(`> ${getEscapedCommand(`az`, keyvaultRoleAssignmentArgs)}`)
await execa(
'az',
Expand All @@ -135,6 +168,24 @@ ${environmentConfig.environmentFileBody}
},
)

console.log(`> ${getEscapedCommand(`az`, assignApplicationWritePermissions)}`)
await execa(
'az',
assignApplicationWritePermissions.map((arg) =>
arg === appIdPlaceholder ? appObjectId : arg,
),
{
stdio: 'inherit',
},
)

console.log(
`Application link: https://portal.azure.com/?feature.msaljs=false#view/Microsoft_AAD_RegisteredApps/ApplicationMenuBlade/~/Overview/appId/${appClientId}/isMSAApp~/false`,
)
console.log(
`Consent link: https://login.microsoftonline.com/${environmentConfig.tenantId}/adminconsent?client_id=${appClientId}`,
)

console.log(`🎉 Success 🎉`)
console.log(
`🎉 Ensure you copy the credentials, the secret will not be stored in ${environmentConfig.environmentFile} 🎉`,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,12 +80,6 @@ variable "cost_centre_tag" {
type = string
}

variable "github_service_principal" {
description = "The name of the github service principal which deploys this environment (if configured through NX)."
type = string
nullable = true
}

variable "github_service_principal_id" {
description = "The id of the github service principal which deploys this environment (if configured through NX)."
type = string
Expand Down