You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: README.md
+236-2Lines changed: 236 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -30,7 +30,8 @@ To be successful in recreating the use cases supported by this pipeline, there a
30
30
- Completion of all pre-requisites and configuration steps leading to [Feature Development](https://github.com/pingidentity/pipeline-example-platform?tab=readme-ov-file#feature-development) from the example-pipeline-platform repository
31
31
-[Docker](https://docs.docker.com/engine/install/) - used to deploy the UI for a sample interface
32
32
-[terraform](https://developer.hashicorp.com/terraform/install) - HashiCorp Terraform (version 1.9.8 was used in this guide)
33
-
-[opa](https://www.openpolicyagent.org/docs/latest/#running-opa) - Open Policy Agent for policy enforcement (version 0.70.0 was used in this guide)
33
+
-[opa](https://developer.hashicorp.com/terraform/tutorials/policy/sentinel-install) - Open Policy Agent for policy enforcement example (version 0.28.0 was used in this guide)
34
+
-[regal](https://github.com/StyraInc/regal) - for OPA policy syntax validation and linting (version 0.29.2 was used in this guide)
34
35
-[tflint](https://github.com/terraform-linters/tflint) - for Terraform linting (version 0.53.0 was used in this guide)
35
36
-[dvlint](https://github.com/pingidentity/dvlint) - for Davinci flow linting (version 1.0.3 was used in this guide)
36
37
-[trivy](https://github.com/aquasecurity/trivy) - for security scanning (version 0.56.2 was used in this guide)
@@ -39,7 +40,7 @@ To be successful in recreating the use cases supported by this pipeline, there a
39
40
-[jq](https://jqlang.github.io/jq/download/) - for JSON parsing (version 1.7.1 was used in this guide)
40
41
41
42
> [!TIP]
42
-
> The last six tools are used by the pipeline in Github, and the pipeline will fail if these tests and configuration checks do not pass. To help ensure the pipeline instance of these tools passes, install these tools locally and run `make devcheck` before committing changes
43
+
> The last six tools are used by the pipeline in Github, and the pipeline will fail if these tests and configuration checks do not pass. Installing these tools locally and running `make devcheck` before committing changes should ensure that the pipeline will pass when changes are pushed. To explore policy enforcement with the `opa` and `regal` tools, see **Policies in CICD** under the ***Advanced Topics*** section at the end of this document.
43
44
44
45
<!-- TODO - Review Required Permissions-->
45
46
> [!IMPORTANT]
@@ -294,3 +295,236 @@ After the development resources are destroyed:
294
295
## Conclusion
295
296
296
297
This repository demonstrates a simplified example of how a pipeline can be used to manage the development and deployment of an application that relies on services managed by a central IAM platform team. The pipeline is designed to be flexible and extensible, allowing for the addition of new features and services as needed. By following the steps outlined in this repository, you can gain a better understanding of how to implement a similar pipeline in your own environment.
298
+
299
+
## Advanced Topics
300
+
301
+
### Policies in CICD
302
+
303
+
While not a requirement for running a CICD pipeline, an emerging best practice for application and infrastructure automation is to include policy enforcement. One such tool in this area is the Open Policy Agent, or **opa**. This demonstration repository includes an example of the use of `opa` in a pipeline. The policy is defined in the `opa` directory. When a pull request is opened against either the **qa** or **prod** branches, an additional step is included in the pipeline that will test the planned changes in the infrastructure against a sample policy that is provided for your exploration.
304
+
305
+
The files provided for deploying the application will pass the policy check as provided. If you want to observe how the policies work, see the **Policy Enforcement** section below to interact with the policies using a local script. As with the developer flow for working with Terraform, testing against any policies must be done locally prior to pushing changes to the repository. Automating the policy checks on the development branch is not possible due to the need for manual input of the environment ID.
306
+
307
+
The example policy performs two checks against the Terraform plan:
308
+
309
+
- Ensure that the `name` attribute of any `davinci_flow` resource to be created or updated starts with the string `AppTeam`. The example use case is our fictitious company wants the name of the team to be reflected in every flow name for easy identification.
310
+
- Ensure that the `deploy` flag is set to `true` for any `davinci_flow` resource to be created or updated. The use case here is to ensure that all flows are deployed when they are created or updated.
311
+
312
+
> [!NOTE]
313
+
> Other policies may be added to this repository in the future. These policies are provided as examples to help you get started with policy enforcement in your own pipelines.
314
+
315
+
#### OPA Overview
316
+
317
+
As stated on their [website](https://www.openpolicyagent.org/), Open Policy Agent (OPA) *is an open source, general-purpose policy engine that unifies policy enforcement across the stack. OPA provides a high-level declarative language that lets you specify policy as code and simple APIs to offload policy decision-making from your software. You can use OPA to enforce policies in microservices, Kubernetes, CI/CD pipelines, API gateways, and more.*
318
+
319
+
OPA works by querying input, provided as JSON, against a policy written in the Rego language. The policy is a set of rules that define what is allowed or denied based on the input. Using the declarative language allows non-developers to create policies to meet their requirements.
320
+
321
+
For running OPA with terraform, the process is as follows:
322
+
323
+
- Policy files are written in the Rego language
324
+
- A `terraform plan` is generated and written to a file
325
+
- The plan output file is converted to json using `terraform show`
326
+
- The converted json output is passed to the `opa` command along with the policy files, where the planned changes are evaluated
327
+
- The `opa` command returns a pass or fail result based on the policy evaluation
328
+
329
+
#### Sample Policy Files
330
+
331
+
The files provided in this repository are arranged according to best practices for rego design, including the directory structure:
332
+
333
+
```bash
334
+
./opa
335
+
└── terraform
336
+
├── davinci
337
+
│ ├── davinci_flow.rego
338
+
│ └── davinci_flow_test.rego
339
+
├── flow_checks
340
+
│ └── flow_checks.rego
341
+
└── library
342
+
├── library.rego
343
+
└── library_test.rego
344
+
```
345
+
346
+
The package names are reflected in the directory structure. For example, the davinci_flow package name is `terraform.davinci`, matching the directory and subdirectory in which the file is located. The library files are common terraform routines that can be used across multiple policies. The `flow_checks` directory contains functions written specifically to filter flows and perform generic policy rules evaluation. The `davinci` directory contains the specific implementation of the policies run against `davinci_flow` resources. The files ending with `_test.rego` are used to validate policies against sample input.
347
+
348
+
An examination of the files will show that there is a hierarchy of imports. The `library` files are imported by the `flow_checks` files, which are imported by the `davinci_flow` files. This hierarchy allows for the reuse of common functions across multiple policies.
349
+
350
+
Further investigation of the files is recommended, but to emphasize the simplicity of the policy enforcement, the `davinci_flow.rego` file is provided below:
351
+
352
+
```bash
353
+
# DISCLAIMER: This file is intended for demonstration and example purposes only.
354
+
# It is provided "as is" without any warranties or guarantees of accuracy or fitness for use.
355
+
# Use at your own risk and adapt to your specific requirements before production use.
356
+
357
+
package terraform.davinci
358
+
359
+
import data.terraform.flow_checks
360
+
import rego.v1
361
+
362
+
resources := input.resource_changes # Explicitly access resources from input
363
+
364
+
# Retrieve all relevant flows with the "create" or "update" action
# description: Determine if all 'davinci_flow' resources meet the required conditions.
382
+
# entrypoint: true
383
+
deny[msg] if {
384
+
not deploy_true
385
+
msg := "All 'davinci_flow' resources must have 'deploy' set to true."
386
+
# print("Deny triggered for deploy:", msg) # Debugging output
387
+
}
388
+
389
+
# Deny if any flow name does not start with "AppTeam"
390
+
deny[msg] if {
391
+
not name_starts_with_appteam
392
+
msg := "All 'davinci_flow' resources must have names starting with 'AppTeam'."
393
+
# print("Deny triggered for name prefix:", msg) # Debugging output
394
+
}
395
+
```
396
+
397
+
#### Policy Testing
398
+
399
+
To test the policies, a wrapper script is provided. "Testing" in this instance refers to validation that the rules are operating as expected. To run the tests, execute the following command:
400
+
401
+
```bash
402
+
./scripts/policy_utils.sh
403
+
```
404
+
405
+
Output:
406
+
407
+
```bash
408
+
No options provided. Defaulting to running the sample OPA policy tests...
409
+
Use --help for more options.
410
+
Running tests...
411
+
PASS: 8/8
412
+
```
413
+
414
+
For a verbose output, pass in either the `--verbose` or `-v` flag:
415
+
416
+
```bash
417
+
./scripts/policy_utils.sh
418
+
419
+
+ shift
420
+
+ test -z ''
421
+
+ '[' true = true ']'
422
+
+ set -x
423
+
+ '[' false = false ']'
424
+
+ echo 'No options provided. Defaulting to running the sample OPA policy tests...'
425
+
No options provided. Defaulting to running the sample OPA policy tests...
To see the policy enforcement in action, a new DaVinci flow would need to be created, or an existing one modified. As an example of a create, the following command was used. The command generated a Terraform plan, converted it to JSON, and ran the policy checks against the plan. If you want to run it on your own, you will have had to successfully run the `./scripts/local_feature_deploy.sh` script at least once to create a state file in the S3 bucket, and supply Terraform files to update or create the tested object. The state file is required to run `terraform plan` successfully.
453
+
454
+
```bash
455
+
./scripts/policy_utils.sh --plan-and-eval
456
+
```
457
+
458
+
Truncated output:
459
+
460
+
```bash
461
+
Initializing the backend...
462
+
Initializing modules...
463
+
Initializing provider plugins...
464
+
465
+
...
466
+
467
+
var.pingone_target_environment_id
468
+
The target environment id to which to deploy the application
469
+
470
+
Enter a value: <environment_id>
471
+
472
+
473
+
Terraform will perform the following actions:
474
+
475
+
...
476
+
477
+
# davinci_flow.registration_flow will be created
478
+
+ resource "davinci_flow" "registration_flow" {
479
+
+ deploy = true
480
+
+ description = "Imported on Tue May 16 2023 19:35:07 GMT+0000 (Coordinated Universal Time)"
Notice the name of the flow and the deploy flag. The policy check passed because the name started with `AppTeam` and the deploy flag was set to `true`.
500
+
501
+
In the case of a failed test against the policy, the output will indicate the failure and the reason for the failure. In the pipeline, it creates a failure to stop deployment. To see this in action, the **./opa/plan.json** file resource_changes block was modified. The davinci_flow resource `deploy` flag was set to `false`, and the name of the flow changed from "AppTeam PingOne DaVinci Registration Example" to "xxxAppTeam PingOne DaVinci Registration Example". Running the script again with the appropriate flag to only perform the evaluation step resulted in the following:
"All 'davinci_flow' resources must have 'deploy' set to true.": true,
518
+
"All 'davinci_flow' resources must have names starting with 'AppTeam'.": true
519
+
}
520
+
```
521
+
522
+
#### Policy Linting
523
+
524
+
The sample policies were linted using the `regal` utility. Linting is a best practice that ensures that all developers are creating files that meet a known standard. If you are going to be developing policies, it is recommended that to lint the files before committing them to the repository. To lint the files, run the following command:
525
+
526
+
```bash
527
+
regal lint ./opa
528
+
```
529
+
530
+
For more information on the `regal` utility, see the link referenced in the prerequisites section.
0 commit comments