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

Commit

Permalink
Added in --leaveFirewallExceptions and --quick arguments for apply pl…
Browse files Browse the repository at this point in the history
…us generator to add firewall exceptions
  • Loading branch information
JakeGinnivan committed Jul 12, 2022
1 parent b2f8a98 commit c0dd855
Show file tree
Hide file tree
Showing 13 changed files with 185 additions and 56 deletions.
5 changes: 5 additions & 0 deletions .changeset/violet-forks-trade.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@arkahna/nx-terraform': minor
---

Introduce --quick mode for terraform apply
5 changes: 5 additions & 0 deletions .changeset/warm-pots-pull.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@arkahna/nx-terraform': minor
---

Added add-firewall-exceptions generator
9 changes: 8 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
{
"editor.formatOnSave": true,
"eslint.validate": ["json"]
"typescript.preferences.importModuleSpecifier": "project-relative",
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.codeActionsOnSave": {
"source.organizeImports": true
},
"eslint.validate": [
"json"
]
}
8 changes: 8 additions & 0 deletions libs/nx-terraform/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,3 +89,11 @@ pnpm nx g \
--environment <environmentname>
```

## Executors

### Apply

`nx apply <tf-project> --environment <env>`

If you are running apply multiple times locally, run with `--leaveFirewallExceptions` to leave the firewall exceptions intact, then run with `--quick` to skip adding firewall rules, running init and skipping refresh during the plan phase.
7 changes: 6 additions & 1 deletion libs/nx-terraform/generators.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@
"factory": "./src/generators/create-azure-environment/generator",
"schema": "./src/generators/create-azure-environment/schema.json",
"description": "Creates an environment for an Azure workload"
},
"add-firewall-exceptions": {
"factory": "./src/generators/add-firewall-exceptions/generator",
"schema": "./src/generators/add-firewall-exceptions/schema.json",
"description": "Adds a firewall rule for the current IP to azure resources"
}
}
}
}
3 changes: 3 additions & 0 deletions libs/nx-terraform/src/common/isDryRun.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export function isDryRun(): boolean {
return process.argv.some((x) => x === '--dry-run')
}
86 changes: 37 additions & 49 deletions libs/nx-terraform/src/executors/apply/executor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,7 @@ import { readConfigFromEnvFile } from '../../common/readConfigFromEnvFile'
import { removeFirewallRules } from '../../common/removeFirewallRules'
import { ApplyExecutorSchema } from './schema'

export default async function runExecutor(
options: ApplyExecutorSchema,
context: ExecutorContext
) {
export default async function runExecutor(options: ApplyExecutorSchema, context: ExecutorContext) {
const publicIpv4 = await publicIp.v4()
const projectName = context.projectName
if (!projectName) {
Expand All @@ -32,42 +29,33 @@ export default async function runExecutor(

const repoConfig = readRepoSettings()

const config = await readConfigFromEnvFile(
repoConfig.terraformStateType,
options.environment
)
const config = await readConfigFromEnvFile(repoConfig.terraformStateType, options.environment)

if (!config) {
console.warn('Skipped apply, no terragrunt file for environment')
return {
success: true,
}
}
const {
resourceGroupName,
terraformStorageAccount,
keyVaultName,
terragruntConfigFile,
} = config
const { resourceGroupName, terraformStorageAccount, keyVaultName, terragruntConfigFile } =
config

const kvOptions = options.addIpToKeyVaults || []
const storageOptions = options.addIpToStorage || []
const {
keyVaultsToRemoveFirewallRules,
storageAccountsToRemoveFirewallRules,
} = await addFirewallRules({
resourceGroupName,
addIpToKeyVaults: options.addIpToDefaultKeyVault
? [keyVaultName, ...kvOptions]
: kvOptions,
addIpToStorageAccounts:
options.addIpToDefaultStorage && terraformStorageAccount
? [terraformStorageAccount, ...storageOptions]
: storageOptions,
publicIpv4,
terragruntConfigFile,
projectRoot,
})
const { keyVaultsToRemoveFirewallRules, storageAccountsToRemoveFirewallRules } =
await addFirewallRules({
resourceGroupName,
addIpToKeyVaults: options.addIpToDefaultKeyVault
? [keyVaultName, ...kvOptions]
: kvOptions,
addIpToStorageAccounts:
options.addIpToDefaultStorage && terraformStorageAccount
? [terraformStorageAccount, ...storageOptions]
: storageOptions,
publicIpv4,
terragruntConfigFile,
projectRoot,
})

const terragruntCliArgs = createTerragruntCliArgs([
...(options.variables || []),
Expand All @@ -81,34 +69,34 @@ export default async function runExecutor(
terragruntConfigFile,
...terragruntCliArgs,
...(options.tfTarget ? ['-target', options.tfTarget] : []),
...(options.quick ? ['-refresh=false'] : []),
]

try {
await initEnvironmentWorkspaceWithFirewallRuleRetry({
terragruntConfigFile,
terragruntCliArgs,
projectRoot,
retryAttempts: options.firewallRetryAttempts,
retryDelay: options.firewallRetryDelay,
})
if (!options.quick) {
await initEnvironmentWorkspaceWithFirewallRuleRetry({
terragruntConfigFile,
terragruntCliArgs,
projectRoot,
retryAttempts: options.firewallRetryAttempts,
retryDelay: options.firewallRetryDelay,
})
}

console.log(
`${projectRoot}> ${getEscapedCommand(
`terragrunt`,
terragruntArguments
)}`
)
console.log(`${projectRoot}> ${getEscapedCommand(`terragrunt`, terragruntArguments)}`)
await execa('terragrunt', terragruntArguments, {
stdio: [process.stdin, process.stdout, 'pipe'],
cwd: projectRoot,
})
} finally {
await removeFirewallRules({
resourceGroupName,
removeIpFromKeyVaults: keyVaultsToRemoveFirewallRules,
removeIpFromStorageAccounts: storageAccountsToRemoveFirewallRules,
publicIpv4,
})
if (options.leaveFirewallExceptions !== true) {
await removeFirewallRules({
resourceGroupName,
removeIpFromKeyVaults: keyVaultsToRemoveFirewallRules,
removeIpFromStorageAccounts: storageAccountsToRemoveFirewallRules,
publicIpv4,
})
}
}

return {
Expand Down
2 changes: 2 additions & 0 deletions libs/nx-terraform/src/executors/apply/schema.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,6 @@ export interface ApplyExecutorSchema {
firewallRetryAttempts: number
firewallRetryDelay: number
tfTarget?: string
quick?: boolean
leaveFirewallExceptions?: boolean
}
10 changes: 9 additions & 1 deletion libs/nx-terraform/src/executors/apply/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,17 @@
"tfTarget": {
"type": "string",
"description": "The target resource to deploy"
},
"quick": {
"type": "boolean",
"description": "Quick apply, skips adding firewall exceptions, init and doesn't refresh the tf state"
},
"leaveFirewallExceptions": {
"type": "boolean",
"description": "Leave firewall exceptions in place after apply"
}
},
"required": [
"environment"
]
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { Tree } from '@nrwl/devkit'
import publicIp from 'public-ip'
import { addFirewallRules } from '../../common/addFirewallRules'
import { isDryRun } from '../../common/isDryRun'
import { readRepoSettings } from '../../common/read-repo-settings'
import { readConfigFromEnvFile } from '../../common/readConfigFromEnvFile'
import { NxTerraformAddFirewallExceptionsSchema } from './schema'

export default async function (tree: Tree, options: NxTerraformAddFirewallExceptionsSchema) {
const repoSettings = readRepoSettings()

if (isDryRun()) {
console.log(
`Firewall exceptions will be added to the environment ${options.environmentName}`,
)
}

return async () => {
const publicIpv4 = await publicIp.v4()
const config = await readConfigFromEnvFile(
repoSettings.terraformStateType,
options.environmentName,
)
if (!config) {
console.warn('Skipped apply, no terragrunt file for environment')
return {
success: true,
}
}
const { resourceGroupName, terraformStorageAccount, keyVaultName, terragruntConfigFile } =
config

const kvOptions = options.addIpToKeyVaults || []
const storageOptions = options.addIpToStorage || []
await addFirewallRules({
resourceGroupName,
addIpToKeyVaults: options.addIpToDefaultKeyVault
? [keyVaultName, ...kvOptions]
: kvOptions,
addIpToStorageAccounts:
options.addIpToDefaultStorage && terraformStorageAccount
? [terraformStorageAccount, ...storageOptions]
: storageOptions,
publicIpv4,
terragruntConfigFile,
// This generator isn't tied to a project, so it doesn't support looking up tf resources
projectRoot: process.cwd(),
})
console.log('🎉 Success 🎉')
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export interface NxTerraformAddFirewallExceptionsSchema {
environmentName: string
addIpToDefaultKeyVault: boolean
addIpToDefaultStorage: boolean
addIpToKeyVaults?: string[]
addIpToStorage?: string[]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{
"$schema": "http://json-schema.org/schema",
"cli": "nx",
"$id": "AddProjectEnvironment",
"title": "",
"type": "object",
"properties": {
"environmentName": {
"type": "string",
"description": "The name of the environment to add firewall exceptions.",
"alias": "e",
"x-prompt": "What is the name of the environment you want to add firewall exceptions for?"
},
"addIpToDefaultKeyVault": {
"type": "boolean",
"description": "Adds a firewall exception for the current ip to the environment keyvault",
"default": true
},
"addIpToDefaultStorage": {
"type": "boolean",
"description": "Adds a firewall exception for the current ip to the environment keyvault",
"default": true
},
"addIpToKeyVaults": {
"type": "array",
"items": {
"type": "string"
},
"description": "Adds a firewall exception for the current ip to specified keyvaults. Specify as a semicolon-delimited list of resource names, e.g. 'ark-dev-akv-system1;ark-dev-akv-system2'."
},
"addIpToStorage": {
"type": "array",
"items": {
"type": "string"
},
"description": "Adds a firewall exception for the current ip to specified storage accounts. Specify as a semicolon-delimited list of resource names, e.g. 'arkdevsta001;arkdevsta001'."
}
},
"required": [
"projectName",
"environmentName"
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { ensureNetworkingExists } from './ensureNetworkingExists'
import { ensureResourceGroupExists } from './ensureResourceGroupExists'
import { ensureResourceNameDefaults } from './ensureResourceNameDefaults'
import { ensureTfStorageAccountExists } from './ensureTfStorageAccountExists'
import { isDryRun } from '../../common/isDryRun'
import { NxTerraformAddEnvironmentSchema } from './schema'

export default async function (tree: Tree, options: NxTerraformAddEnvironmentSchema) {
Expand Down Expand Up @@ -187,7 +188,3 @@ async function createWorkloadEnvironmentStorage(
)
}
}

export function isDryRun(): boolean {
return process.argv.some((x) => x === '--dry-run')
}

0 comments on commit c0dd855

Please sign in to comment.