diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9bea433 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ + +.DS_Store diff --git a/api-pushCode.py b/api-pushCode.py deleted file mode 100644 index 62d4541..0000000 --- a/api-pushCode.py +++ /dev/null @@ -1,36 +0,0 @@ -import requests -import tarfile -import os -import json -tfeURL = "https://ptfe.this-demo.rocks" -org = "SE_Org" -AtlasToken = os.environ["PTFE_TOKEN"] -workspaceName = "VMWorld-Staging" -headers = {'Authorization': "Bearer " + AtlasToken, 'Content-Type' : 'application/vnd.api+json'} -workspaceID_URL = tfeURL + "/api/v2/organizations/" + org + "/workspaces/" + workspaceName - -configVersion = json.dumps({"data":{"type":"configuration-version"}}) - -# Get Current Working Directory -cwd = os.getcwd() - -def make_tarfile(source_dir): - with tarfile.open("./terraConfig.tar.gz", "w:gz") as tar: - tar.add(source_dir, arcname=os.path.sep) - return(source_dir + "/terraConfig.tar.gz") - -tfeWorkspaceID = requests.get(workspaceID_URL, headers=headers) -workspaceID = json.loads(tfeWorkspaceID.text).get('data').get('id') -tfeUploadURL = tfeURL + "/api/v2/workspaces/" + workspaceID + "/configuration-versions" -uploadURL_JSON = requests.post(tfeUploadURL,data=configVersion, headers=headers) -uploadURL = json.loads(uploadURL_JSON.text).get('data').get('attributes').get('upload-url') -filename = make_tarfile(cwd) - -with open(filename, 'rb') as data: - results = requests.put(uploadURL, data=data) - -os.remove(filename) -print(results.status_code) - - - diff --git a/aws_config.tf b/aws_config.tf index 3ba682e..78c6d87 100644 --- a/aws_config.tf +++ b/aws_config.tf @@ -1,7 +1,7 @@ provider "aws" { - access_key = "${var.aws_access_key}" - secret_key = "${var.aws_secret_key}" - region = "${var.aws_region}" + access_key = var.aws_access_key + secret_key = var.aws_secret_key + region = var.aws_region } resource "aws_vpc" "main" { @@ -9,7 +9,7 @@ resource "aws_vpc" "main" { } resource "aws_subnet" "development" { - vpc_id = "${aws_vpc.main.id}" + vpc_id = aws_vpc.main.id cidr_block = "10.0.1.0/24" tags = { @@ -17,17 +17,17 @@ resource "aws_subnet" "development" { } } -resource "aws_subnet" "staging" { - vpc_id = "${aws_vpc.main.id}" +resource "aws_subnet" "stage" { + vpc_id = aws_vpc.main.id cidr_block = "10.0.2.0/24" tags = { - Name = "staging" + Name = "stage" } } resource "aws_subnet" "production" { - vpc_id = "${aws_vpc.main.id}" + vpc_id = aws_vpc.main.id cidr_block = "10.0.3.0/24" tags = { diff --git a/main.tf b/main.tf index 2b6148f..2996ecd 100644 --- a/main.tf +++ b/main.tf @@ -1,16 +1,16 @@ provider "tfe" { - hostname = "${var.hostname}" - token = "${var.token}" + hostname = var.hostname + token = var.token } resource "tfe_team" "developers" { name = "${var.use_case_name}-developers" - organization = "${var.org}" + organization = var.org } resource "tfe_team" "ops" { name = "${var.use_case_name}-production" - organization = "${var.org}" + organization = var.org } resource "tfe_team_member" "dev-user" { @@ -29,10 +29,10 @@ resource "tfe_team_access" "development-dev" { workspace_id = "${tfe_workspace.development.id}" } -resource "tfe_team_access" "staging-dev" { +resource "tfe_team_access" "stage-dev" { access = "write" team_id = "${tfe_team.developers.id}" - workspace_id = "${tfe_workspace.staging.id}" + workspace_id = "${tfe_workspace.stage.id}" } resource "tfe_team_access" "production-dev" { @@ -47,10 +47,10 @@ resource "tfe_team_access" "production-ops" { workspace_id = "${tfe_workspace.production.id}" } -resource "tfe_team_access" "staging-ops" { +resource "tfe_team_access" "stage-ops" { access = "admin" team_id = "${tfe_team.ops.id}" - workspace_id = "${tfe_workspace.staging.id}" + workspace_id = "${tfe_workspace.stage.id}" } resource "tfe_team_access" "development-ops" { @@ -61,53 +61,53 @@ resource "tfe_team_access" "development-ops" { resource "tfe_workspace" "development" { name = "${var.use_case_name}-development" - organization = "${var.org}" + organization = var.org auto_apply = true queue_all_runs = false - terraform_version = "0.12.7" + terraform_version = "1.3.5" vcs_repo { branch = "development" - identifier = "${var.vcs_identifier}" - oauth_token_id = "${var.oauth_token}" + identifier = var.vcs_identifier + oauth_token_id = var.oauth_token } } -resource "tfe_workspace" "staging" { - name = "${var.use_case_name}-staging" - organization = "${var.org}" +resource "tfe_workspace" "stage" { + name = "${var.use_case_name}-stage" + organization = var.org auto_apply = true - terraform_version = "0.12.7" + terraform_version = "1.3.5" vcs_repo { - branch = "staging" - identifier = "${var.vcs_identifier}" - oauth_token_id = "${var.oauth_token}" + branch = "stage" + identifier = var.vcs_identifier + oauth_token_id = var.oauth_token } } resource "tfe_workspace" "production" { name = "${var.use_case_name}-production" - organization = "${var.org}" - terraform_version = "0.12.7" + organization = var.org + terraform_version = "1.3.5" vcs_repo { - branch = "master" - identifier = "${var.vcs_identifier}" - oauth_token_id = "${var.oauth_token}" + branch = "main" + identifier = var.vcs_identifier + oauth_token_id = var.oauth_token } } resource "tfe_variable" "staging_aws_access_key" { key = "AWS_ACCESS_KEY_ID" - value = "${var.aws_access_key}" + value = var.aws_access_key category = "env" - workspace_id = "${tfe_workspace.staging.id}" + workspace_id = "${tfe_workspace.stage.id}" } resource "tfe_variable" "development_aws_access_key" { key = "AWS_ACCESS_KEY_ID" - value = "${var.aws_access_key}" + value = var.aws_access_key category = "env" workspace_id = "${tfe_workspace.development.id}" } @@ -124,7 +124,7 @@ resource "tfe_variable" "staging_aws_secret_key" { value = "${var.aws_secret_key}" category = "env" sensitive = "true" - workspace_id = "${tfe_workspace.staging.id}" + workspace_id = "${tfe_workspace.stage.id}" } resource "tfe_variable" "development_aws_secret_key" { @@ -148,7 +148,7 @@ resource "tfe_variable" "workspace_var_staging" { value = "${var.creator_workspace}" category = "terraform" - workspace_id = "${tfe_workspace.staging.id}" + workspace_id = "${tfe_workspace.stage.id}" } resource "tfe_variable" "workspace_var_development" { @@ -185,7 +185,7 @@ resource "tfe_variable" "confirm_destroy2" { key = "CONFIRM_DESTROY" value = "1" category = "env" - workspace_id = "${tfe_workspace.staging.id}" + workspace_id = "${tfe_workspace.stage.id}" } resource "tfe_variable" "confirm_destroy3" { @@ -206,7 +206,7 @@ resource "tfe_variable" "set_ttl2" { key = "WORKSPACE_TTL" value = "30" category = "env" - workspace_id = "${tfe_workspace.staging.id}" + workspace_id = "${tfe_workspace.stage.id}" } resource "tfe_variable" "set_ttl3" { @@ -225,10 +225,10 @@ resource "tfe_variable" "org_var_development" { resource "tfe_variable" "org_var_staging" { key = "org" - value = "${var.org}" + value = var.org category = "terraform" - workspace_id = "${tfe_workspace.staging.id}" + workspace_id = "${tfe_workspace.stage.id}" } resource "tfe_variable" "environment_name_dev" { @@ -244,7 +244,7 @@ resource "tfe_variable" "environment_name_stage" { value = "stage" category = "terraform" - workspace_id = "${tfe_workspace.staging.id}" + workspace_id = "${tfe_workspace.stage.id}" } resource "tfe_variable" "environment_name_prod" { @@ -257,21 +257,21 @@ resource "tfe_variable" "environment_name_prod" { resource "tfe_variable" "name_dev" { key = "name" - value = "${var.use_case_name}" + value = var.use_case_name category = "terraform" workspace_id = "${tfe_workspace.development.id}" } -resource "tfe_variable" "name_staging" { +resource "tfe_variable" "name_stage" { key = "name" - value = "${var.use_case_name}" + value = var.use_case_name category = "terraform" - workspace_id = "${tfe_workspace.staging.id}" + workspace_id = "${tfe_workspace.stage.id}" } resource "tfe_variable" "name_prod" { key = "name" - value = "${var.use_case_name}" + value = var.use_case_name category = "terraform" workspace_id = "${tfe_workspace.production.id}" } diff --git a/outputs.tf b/outputs.tf index 5cf01f5..a91d935 100644 --- a/outputs.tf +++ b/outputs.tf @@ -11,7 +11,7 @@ output "development_subnet_id" { } output "staging_subnet_id" { - value = "${aws_subnet.staging.id}" + value = "${aws_subnet.stage.id}" } output "production_subnet_id" { diff --git a/sentinel/LICENSE b/sentinel/LICENSE deleted file mode 100644 index c33dcc7..0000000 --- a/sentinel/LICENSE +++ /dev/null @@ -1,354 +0,0 @@ -Mozilla Public License, version 2.0 - -1. Definitions - -1.1. “Contributor” - - means each individual or legal entity that creates, contributes to the - creation of, or owns Covered Software. - -1.2. “Contributor Version” - - means the combination of the Contributions of others (if any) used by a - Contributor and that particular Contributor’s Contribution. - -1.3. “Contribution” - - means Covered Software of a particular Contributor. - -1.4. “Covered Software” - - means Source Code Form to which the initial Contributor has attached the - notice in Exhibit A, the Executable Form of such Source Code Form, and - Modifications of such Source Code Form, in each case including portions - thereof. - -1.5. “Incompatible With Secondary Licenses” - means - - a. that the initial Contributor has attached the notice described in - Exhibit B to the Covered Software; or - - b. that the Covered Software was made available under the terms of version - 1.1 or earlier of the License, but not also under the terms of a - Secondary License. - -1.6. “Executable Form” - - means any form of the work other than Source Code Form. - -1.7. “Larger Work” - - means a work that combines Covered Software with other material, in a separate - file or files, that is not Covered Software. - -1.8. “License” - - means this document. - -1.9. “Licensable” - - means having the right to grant, to the maximum extent possible, whether at the - time of the initial grant or subsequently, any and all of the rights conveyed by - this License. - -1.10. “Modifications” - - means any of the following: - - a. any file in Source Code Form that results from an addition to, deletion - from, or modification of the contents of Covered Software; or - - b. any new file in Source Code Form that contains any Covered Software. - -1.11. “Patent Claims” of a Contributor - - means any patent claim(s), including without limitation, method, process, - and apparatus claims, in any patent Licensable by such Contributor that - would be infringed, but for the grant of the License, by the making, - using, selling, offering for sale, having made, import, or transfer of - either its Contributions or its Contributor Version. - -1.12. “Secondary License” - - means either the GNU General Public License, Version 2.0, the GNU Lesser - General Public License, Version 2.1, the GNU Affero General Public - License, Version 3.0, or any later versions of those licenses. - -1.13. “Source Code Form” - - means the form of the work preferred for making modifications. - -1.14. “You” (or “Your”) - - means an individual or a legal entity exercising rights under this - License. For legal entities, “You” includes any entity that controls, is - controlled by, or is under common control with You. For purposes of this - definition, “control” means (a) the power, direct or indirect, to cause - the direction or management of such entity, whether by contract or - otherwise, or (b) ownership of more than fifty percent (50%) of the - outstanding shares or beneficial ownership of such entity. - - -2. License Grants and Conditions - -2.1. Grants - - Each Contributor hereby grants You a world-wide, royalty-free, - non-exclusive license: - - a. under intellectual property rights (other than patent or trademark) - Licensable by such Contributor to use, reproduce, make available, - modify, display, perform, distribute, and otherwise exploit its - Contributions, either on an unmodified basis, with Modifications, or as - part of a Larger Work; and - - b. under Patent Claims of such Contributor to make, use, sell, offer for - sale, have made, import, and otherwise transfer either its Contributions - or its Contributor Version. - -2.2. Effective Date - - The licenses granted in Section 2.1 with respect to any Contribution become - effective for each Contribution on the date the Contributor first distributes - such Contribution. - -2.3. Limitations on Grant Scope - - The licenses granted in this Section 2 are the only rights granted under this - License. No additional rights or licenses will be implied from the distribution - or licensing of Covered Software under this License. Notwithstanding Section - 2.1(b) above, no patent license is granted by a Contributor: - - a. for any code that a Contributor has removed from Covered Software; or - - b. for infringements caused by: (i) Your and any other third party’s - modifications of Covered Software, or (ii) the combination of its - Contributions with other software (except as part of its Contributor - Version); or - - c. under Patent Claims infringed by Covered Software in the absence of its - Contributions. - - This License does not grant any rights in the trademarks, service marks, or - logos of any Contributor (except as may be necessary to comply with the - notice requirements in Section 3.4). - -2.4. Subsequent Licenses - - No Contributor makes additional grants as a result of Your choice to - distribute the Covered Software under a subsequent version of this License - (see Section 10.2) or under the terms of a Secondary License (if permitted - under the terms of Section 3.3). - -2.5. Representation - - Each Contributor represents that the Contributor believes its Contributions - are its original creation(s) or it has sufficient rights to grant the - rights to its Contributions conveyed by this License. - -2.6. Fair Use - - This License is not intended to limit any rights You have under applicable - copyright doctrines of fair use, fair dealing, or other equivalents. - -2.7. Conditions - - Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in - Section 2.1. - - -3. Responsibilities - -3.1. Distribution of Source Form - - All distribution of Covered Software in Source Code Form, including any - Modifications that You create or to which You contribute, must be under the - terms of this License. You must inform recipients that the Source Code Form - of the Covered Software is governed by the terms of this License, and how - they can obtain a copy of this License. You may not attempt to alter or - restrict the recipients’ rights in the Source Code Form. - -3.2. Distribution of Executable Form - - If You distribute Covered Software in Executable Form then: - - a. such Covered Software must also be made available in Source Code Form, - as described in Section 3.1, and You must inform recipients of the - Executable Form how they can obtain a copy of such Source Code Form by - reasonable means in a timely manner, at a charge no more than the cost - of distribution to the recipient; and - - b. You may distribute such Executable Form under the terms of this License, - or sublicense it under different terms, provided that the license for - the Executable Form does not attempt to limit or alter the recipients’ - rights in the Source Code Form under this License. - -3.3. Distribution of a Larger Work - - You may create and distribute a Larger Work under terms of Your choice, - provided that You also comply with the requirements of this License for the - Covered Software. If the Larger Work is a combination of Covered Software - with a work governed by one or more Secondary Licenses, and the Covered - Software is not Incompatible With Secondary Licenses, this License permits - You to additionally distribute such Covered Software under the terms of - such Secondary License(s), so that the recipient of the Larger Work may, at - their option, further distribute the Covered Software under the terms of - either this License or such Secondary License(s). - -3.4. Notices - - You may not remove or alter the substance of any license notices (including - copyright notices, patent notices, disclaimers of warranty, or limitations - of liability) contained within the Source Code Form of the Covered - Software, except that You may alter any license notices to the extent - required to remedy known factual inaccuracies. - -3.5. Application of Additional Terms - - You may choose to offer, and to charge a fee for, warranty, support, - indemnity or liability obligations to one or more recipients of Covered - Software. However, You may do so only on Your own behalf, and not on behalf - of any Contributor. You must make it absolutely clear that any such - warranty, support, indemnity, or liability obligation is offered by You - alone, and You hereby agree to indemnify every Contributor for any - liability incurred by such Contributor as a result of warranty, support, - indemnity or liability terms You offer. You may include additional - disclaimers of warranty and limitations of liability specific to any - jurisdiction. - -4. Inability to Comply Due to Statute or Regulation - - If it is impossible for You to comply with any of the terms of this License - with respect to some or all of the Covered Software due to statute, judicial - order, or regulation then You must: (a) comply with the terms of this License - to the maximum extent possible; and (b) describe the limitations and the code - they affect. Such description must be placed in a text file included with all - distributions of the Covered Software under this License. Except to the - extent prohibited by statute or regulation, such description must be - sufficiently detailed for a recipient of ordinary skill to be able to - understand it. - -5. Termination - -5.1. The rights granted under this License will terminate automatically if You - fail to comply with any of its terms. However, if You become compliant, - then the rights granted under this License from a particular Contributor - are reinstated (a) provisionally, unless and until such Contributor - explicitly and finally terminates Your grants, and (b) on an ongoing basis, - if such Contributor fails to notify You of the non-compliance by some - reasonable means prior to 60 days after You have come back into compliance. - Moreover, Your grants from a particular Contributor are reinstated on an - ongoing basis if such Contributor notifies You of the non-compliance by - some reasonable means, this is the first time You have received notice of - non-compliance with this License from such Contributor, and You become - compliant prior to 30 days after Your receipt of the notice. - -5.2. If You initiate litigation against any entity by asserting a patent - infringement claim (excluding declaratory judgment actions, counter-claims, - and cross-claims) alleging that a Contributor Version directly or - indirectly infringes any patent, then the rights granted to You by any and - all Contributors for the Covered Software under Section 2.1 of this License - shall terminate. - -5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user - license agreements (excluding distributors and resellers) which have been - validly granted by You or Your distributors under this License prior to - termination shall survive termination. - -6. Disclaimer of Warranty - - Covered Software is provided under this License on an “as is” basis, without - warranty of any kind, either expressed, implied, or statutory, including, - without limitation, warranties that the Covered Software is free of defects, - merchantable, fit for a particular purpose or non-infringing. The entire - risk as to the quality and performance of the Covered Software is with You. - Should any Covered Software prove defective in any respect, You (not any - Contributor) assume the cost of any necessary servicing, repair, or - correction. This disclaimer of warranty constitutes an essential part of this - License. No use of any Covered Software is authorized under this License - except under this disclaimer. - -7. Limitation of Liability - - Under no circumstances and under no legal theory, whether tort (including - negligence), contract, or otherwise, shall any Contributor, or anyone who - distributes Covered Software as permitted above, be liable to You for any - direct, indirect, special, incidental, or consequential damages of any - character including, without limitation, damages for lost profits, loss of - goodwill, work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses, even if such party shall have been - informed of the possibility of such damages. This limitation of liability - shall not apply to liability for death or personal injury resulting from such - party’s negligence to the extent applicable law prohibits such limitation. - Some jurisdictions do not allow the exclusion or limitation of incidental or - consequential damages, so this exclusion and limitation may not apply to You. - -8. Litigation - - Any litigation relating to this License may be brought only in the courts of - a jurisdiction where the defendant maintains its principal place of business - and such litigation shall be governed by laws of that jurisdiction, without - reference to its conflict-of-law provisions. Nothing in this Section shall - prevent a party’s ability to bring cross-claims or counter-claims. - -9. Miscellaneous - - This License represents the complete agreement concerning the subject matter - hereof. If any provision of this License is held to be unenforceable, such - provision shall be reformed only to the extent necessary to make it - enforceable. Any law or regulation which provides that the language of a - contract shall be construed against the drafter shall not be used to construe - this License against a Contributor. - - -10. Versions of the License - -10.1. New Versions - - Mozilla Foundation is the license steward. Except as provided in Section - 10.3, no one other than the license steward has the right to modify or - publish new versions of this License. Each version will be given a - distinguishing version number. - -10.2. Effect of New Versions - - You may distribute the Covered Software under the terms of the version of - the License under which You originally received the Covered Software, or - under the terms of any subsequent version published by the license - steward. - -10.3. Modified Versions - - If you create software not governed by this License, and you want to - create a new license for such software, you may create and use a modified - version of this License if you rename the license and remove any - references to the name of the license steward (except to note that such - modified license differs from this License). - -10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses - If You choose to distribute Source Code Form that is Incompatible With - Secondary Licenses under the terms of this version of the License, the - notice described in Exhibit B of this License must be attached. - -Exhibit A - Source Code Form License Notice - - This Source Code Form is subject to the - terms of the Mozilla Public License, v. - 2.0. If a copy of the MPL was not - distributed with this file, You can - obtain one at - http://mozilla.org/MPL/2.0/. - -If it is not possible or desirable to put the notice in a particular file, then -You may include the notice in a location (such as a LICENSE file in a relevant -directory) where a recipient would be likely to look for such a notice. - -You may add additional accurate notices of copyright ownership. - -Exhibit B - “Incompatible With Secondary Licenses” Notice - - This Source Code Form is “Incompatible - With Secondary Licenses”, as defined by - the Mozilla Public License, v. 2.0. - diff --git a/sentinel/README.md b/sentinel/README.md deleted file mode 100644 index 3b49bc8..0000000 --- a/sentinel/README.md +++ /dev/null @@ -1,173 +0,0 @@ -# Overview -Follow this README to setup a workspace used to demonstrate Sentinel policies being managed via VCS. -This example is based on this article on the Terraform.io docs: https://www.terraform.io/docs/enterprise/sentinel/integrate-vcs.html - -## Assumptions -You have already followed the steps to configure the Producer Consumer demo environments in your TFE SaaS or pTFE instance. -This guide assumes Github is being used for a VCS and that it has already been configured in your TFE org - -Producer -https://github.com/AdamCavaliere/Producer-Repo -Consumer -https://github.com/AdamCavaliere/Consumer-Repo - -# Setup -### Log into TFE -* Log into your TFE or pTFE environment and choose the organization you are working in -### Create a workspace -* Create a workspace called "sentinel_policies" -* Configure VCS to point to your forked copy of the Producer-Repo, (default branch) -* For the sentinel_policies workspace click on Settings > General > - - TERRAFORM WORKING DIRECTORY set it to `sentinel` this will link to the sentinel sub directory in the parent repo -* Configure the following Terraform variables for the workspace: - - tfe_token - - tfe_organization - - tfe_hostname or your pTFE host name - -* Configure the following Environment variables for the workspace: - - CONFIRM_DESTROY < 1 > - -### IF YOU ARE A pTFE user PERFORM THIS STEP -* In the root of the sentinel directory find the `main.tf` file -* Modify main.tf -* If you are using pTFE modify this stanza to include your hostname and organization -``` -terraform { - backend "remote" { - hostname = "" - organization = "" - } -} -``` -### This demo is has some hardcoded values to reference workspaces pre-created via the Producer Consumer demo creation -* While still in the `main.tf` Verify that you are using the workspace names the Producer demo repo set, find these sections to update with your workspace names -``` -resource "tfe_policy_set" "production" { - name = "production" - description = "Policies that should be enforced on production infrastructure." - organization = "${var.tfe_organization}" - - policy_ids = [ - "${tfe_sentinel_policy.aws-restrict-instance-type-prod.id}", - ] - - workspace_external_ids = [ - "${local.workspaces["ExampleTeam-production"]}", - ] -} -``` -* The above stanza is used by the TFE provider to create a Sentinel policy set, in this case called "production" -* The section `workspace_external_ids =` is used to add workspaces to the policy set that will be governed by the policies -* Now we want to hardcode the name of the production workspace you created in the Producer Consumer demo build out -``` -workspace_external_ids = [ - "${local.workspaces["ExampleTeam-production"]}", - ] -``` -* Repeat this hardcode step for the `resource "tfe_policy_set" "development"` -``` -workspace_external_ids = [ - "${local.workspaces["ExampleTeam-development"]}", - ] -``` -* Repeat this hardcode step for the `resource "tfe_policy_set" "development"` -``` -workspace_external_ids = [ - "${local.workspaces["ExampleTeam-staging"]}", - ] -``` -* Repeat this hardcode step for the `resource "tfe_policy_set" "sentinel"` -``` -workspace_external_ids = [ - "${local.workspaces["sentinel_policies"]}", - ] -``` -* Commit the changes to the main.tf -## Test the deployment of sentinel policies -* When you committed the changes to the main.tf in the previous steps that would have kicked off a plan in your sentinel_policies workspace, or your workspace my still be sitting at the initial setup screen for the workspace -* Note that commiting changes to the sentinel directory will also trigger plans on the Producer workspace but should not detect any infrastructure changes. -* Queue a manual run of the `sentinel_policies` workspace -* If you get a successful plan you are in good shape, now apply the run -* Go to the Org Settings for TFE, click on Policies and Policy Sets links to review the sentinel policies created and the policiy sets -* If you didn't get a successful apply verify the hardcoded values in the `sentinel/main.tf` file. -* Now that you know the `sentinel_policies` workspace is functional clean up by running a destroy -* In the `sentinel_policies` workspace click on Settings > Destruction and Deletion > Queue destroy Plan -* You will demo the workspace VCS run later in your demo - -# Post Setup demo info -## Create a new Sentinel policy via the TFE GUI -* Log into the TFE GUI, switch to your Org, then click on upper nav bar > Settings > Policies > Create a new policy - - Policy name: aws-enforce-tags - - Description: A sentinel policy that enforces a certain tag on AWS instances - - Enforcement mode: Hard-mandatory - - Policy Code: -``` -import "tfplan" - -main = rule { - all tfplan.resources.aws_instance as _, instances { - all instances as _, r { - r.applied contains "tags" and - r.applied.tags contains "Name" - } - } -} -``` -* Click Create Policy - -## Create a Policy Set -* Click Policy Sets > Create a new policy set - - NAME `test-policy-set` - - DESCRIPTION `test bed for sentinel policies` - - SCOPE OF POLICIES radio button `Policies enforced on selected workspaces` - - Policies > select from the dropdown `aws_enforce_tags` and click Add Policy - - Workspaces > select from the dropdown `ExampleTeam-development` and click Add Workspace - - Click Update Policy set button -## Modify a consumer environment -* Go to the Consumer-Repo that you cloned during the Producer Consumer demo setup -* Switch to the development branch -* Edit the `main.tf` - - find the `resource "aws_instance" "web" ` stanza - - Comment out the tags section -``` -/* tags { - Name = "Research Instance" - } */ -``` -* Commit the change -## Verify the run on a workspace -* Go to the `ExampleTeam-development` workspace -* The previous commit should have triggered a run -* We should see the plan succeed -* Sentinel check should fail due to the lack of a tag with `Name` -* If this doesn't work check the previous steps -* After a successful plan and check failure, delete this manually created sentinel policy and policy set -* You will now shift into the VCS management of Sentinel policies in tfe - -## Use a workspace to apply and manage Sentinel policies via VCS -* Go to the `sentinel_policies` workspace -* Trigger a manual run via Queue Plan -* You now have some additional policies to demonstrate - -## Production Change Window use case -To help drive home the whole story of Sentinel, VCS, and overall governance use these demo steps -* Go to the `ExampleTeam-production` workspace -* Trigger a run either via a VCS change to the `main.tf` file or via the Queue Plan -* This should pass a plan and policy check, you can discard the apply if there are changes to make to the infrastructure -* Now go to the Producer Repo > Sentinel directory -* Edit the `main.tf` - - Find the `resource "tfe_policy_set" "production"` stanza -* There is a policy that is commented out -``` -policy_ids = [ - "${tfe_sentinel_policy.aws-restrict-instance-type-prod.id}", - -> #"${tfe_sentinel_policy.prod-change-window-hours.id}", - ] -``` -* Un-comment this line to include the `prod-change-windows-hours` sentinel policy -* Commit the changest to `main.tf` -* This will trigger a plan in the `sentinel_policies` workspace -* Accept the apply on this change -* Go back to the `ExampleTeam-production` workspace and trigger a run -* This time the policy check will fail to indicate that you are attempting to make a change to production outside of the approved change window -* You have now demonstrated Sentinel policy creation via the GUI, and via a VCS workflow diff --git a/sentinel/aws-block-allow-all-cidr.sentinel b/sentinel/aws-block-allow-all-cidr.sentinel deleted file mode 100644 index 166339f..0000000 --- a/sentinel/aws-block-allow-all-cidr.sentinel +++ /dev/null @@ -1,56 +0,0 @@ -import "tfplan" - -# Get an array of all resources of the given type (or an empty array). -get_resources = func(type) { - if length(tfplan.module_paths else []) > 0 { # always true in the real tfplan import - return get_resources_all_modules(type) - } else { # fallback for tests - return get_resources_root_only(type) - } -} - -get_resources_root_only = func(type) { - resources = [] - named_and_counted_resources = tfplan.resources[type] else {} - # Get resource bodies out of nested resource maps, from: - # {"name": {"0": {"applied": {...}, "diff": {...} }, "1": {...}}, "name": {...}} - # to: - # [{"applied": {...}, "diff": {...}}, {"applied": {...}, "diff": {...}}, ...] - for named_and_counted_resources as _, instances { - for instances as _, body { - append(resources, body) - } - } - return resources -} - -get_resources_all_modules = func(type) { - resources = [] - for tfplan.module_paths as path { - named_and_counted_resources = tfplan.module(path).resources[type] else {} - # Get resource bodies out of nested resource maps, from: - # {"name": {"0": {"applied": {...}, "diff": {...} }, "1": {...}}, "name": {...}} - # to: - # [{"applied": {...}, "diff": {...}}, {"applied": {...}, "diff": {...}}, ...] - for named_and_counted_resources as _, instances { - for instances as _, body { - append(resources, body) - } - } - } - return resources -} - -disallowed_cidr_blocks = [ - "0.0.0.0/0", -] - -main = rule { - all get_resources("aws_security_group") as sg { - all sg.applied.ingress as ingress { - all disallowed_cidr_blocks as block { - ingress.cidr_blocks not contains block - } - } - } -} \ No newline at end of file diff --git a/sentinel/aws-restrict-instance-type-default.sentinel b/sentinel/aws-restrict-instance-type-default.sentinel deleted file mode 100644 index 03935b7..0000000 --- a/sentinel/aws-restrict-instance-type-default.sentinel +++ /dev/null @@ -1,66 +0,0 @@ -import "tfplan" - -# Get an array of all resources of the given type (or an empty array). -get_resources = func(type) { - if length(tfplan.module_paths else []) > 0 { # always true in the real tfplan import - return get_resources_all_modules(type) - } else { # fallback for tests - return get_resources_root_only(type) - } -} - -get_resources_root_only = func(type) { - resources = [] - named_and_counted_resources = tfplan.resources[type] else {} - # Get resource bodies out of nested resource maps, from: - # {"name": {"0": {"applied": {...}, "diff": {...} }, "1": {...}}, "name": {...}} - # to: - # [{"applied": {...}, "diff": {...}}, {"applied": {...}, "diff": {...}}, ...] - for named_and_counted_resources as _, instances { - for instances as _, body { - append(resources, body) - } - } - return resources -} - -get_resources_all_modules = func(type) { - resources = [] - for tfplan.module_paths as path { - named_and_counted_resources = tfplan.module(path).resources[type] else {} - # Get resource bodies out of nested resource maps, from: - # {"name": {"0": {"applied": {...}, "diff": {...} }, "1": {...}}, "name": {...}} - # to: - # [{"applied": {...}, "diff": {...}}, {"applied": {...}, "diff": {...}}, ...] - for named_and_counted_resources as _, instances { - for instances as _, body { - append(resources, body) - } - } - } - return resources -} - -# Allowed Types -allowed_types = [ - "t2.nano", - "t2.micro", - "t2.small", - "t2.medium", - "t2.large", - "t2.xlarge", - "m4.large", - "m4.xlarge", -] - -# Rule to restrict instance types -instance_type_allowed = rule { - all get_resources("aws_instance") as r { - r.applied.instance_type in allowed_types - } -} - -# Main rule that requires other rules to be true -main = rule { - (instance_type_allowed) else true -} \ No newline at end of file diff --git a/sentinel/aws-restrict-instance-type-dev.sentinel b/sentinel/aws-restrict-instance-type-dev.sentinel deleted file mode 100644 index 6bea257..0000000 --- a/sentinel/aws-restrict-instance-type-dev.sentinel +++ /dev/null @@ -1,62 +0,0 @@ -import "tfplan" - -# Get an array of all resources of the given type (or an empty array). -get_resources = func(type) { - if length(tfplan.module_paths else []) > 0 { # always true in the real tfplan import - return get_resources_all_modules(type) - } else { # fallback for tests - return get_resources_root_only(type) - } -} - -get_resources_root_only = func(type) { - resources = [] - named_and_counted_resources = tfplan.resources[type] else {} - # Get resource bodies out of nested resource maps, from: - # {"name": {"0": {"applied": {...}, "diff": {...} }, "1": {...}}, "name": {...}} - # to: - # [{"applied": {...}, "diff": {...}}, {"applied": {...}, "diff": {...}}, ...] - for named_and_counted_resources as _, instances { - for instances as _, body { - append(resources, body) - } - } - return resources -} - -get_resources_all_modules = func(type) { - resources = [] - for tfplan.module_paths as path { - named_and_counted_resources = tfplan.module(path).resources[type] else {} - # Get resource bodies out of nested resource maps, from: - # {"name": {"0": {"applied": {...}, "diff": {...} }, "1": {...}}, "name": {...}} - # to: - # [{"applied": {...}, "diff": {...}}, {"applied": {...}, "diff": {...}}, ...] - for named_and_counted_resources as _, instances { - for instances as _, body { - append(resources, body) - } - } - } - return resources -} - -# Allowed Types -allowed_types = [ - "t2.nano", - "t2.micro", - "t2.small", - "t2.medium", -] - -# Rule to restrict instance types -instance_type_allowed = rule { - all get_resources("aws_instance") as r { - r.applied.instance_type in allowed_types - } -} - -# Main rule that requires other rules to be true -main = rule { - (instance_type_allowed) else true -} \ No newline at end of file diff --git a/sentinel/aws-restrict-instance-type-prod.sentinel b/sentinel/aws-restrict-instance-type-prod.sentinel deleted file mode 100644 index 75098e8..0000000 --- a/sentinel/aws-restrict-instance-type-prod.sentinel +++ /dev/null @@ -1,64 +0,0 @@ -import "tfplan" - -# Get an array of all resources of the given type (or an empty array). -get_resources = func(type) { - if length(tfplan.module_paths else []) > 0 { # always true in the real tfplan import - return get_resources_all_modules(type) - } else { # fallback for tests - return get_resources_root_only(type) - } -} - -get_resources_root_only = func(type) { - resources = [] - named_and_counted_resources = tfplan.resources[type] else {} - # Get resource bodies out of nested resource maps, from: - # {"name": {"0": {"applied": {...}, "diff": {...} }, "1": {...}}, "name": {...}} - # to: - # [{"applied": {...}, "diff": {...}}, {"applied": {...}, "diff": {...}}, ...] - for named_and_counted_resources as _, instances { - for instances as _, body { - append(resources, body) - } - } - return resources -} - -get_resources_all_modules = func(type) { - resources = [] - for tfplan.module_paths as path { - named_and_counted_resources = tfplan.module(path).resources[type] else {} - # Get resource bodies out of nested resource maps, from: - # {"name": {"0": {"applied": {...}, "diff": {...} }, "1": {...}}, "name": {...}} - # to: - # [{"applied": {...}, "diff": {...}}, {"applied": {...}, "diff": {...}}, ...] - for named_and_counted_resources as _, instances { - for instances as _, body { - append(resources, body) - } - } - } - return resources -} - -# Allowed Types -allowed_types = [ - "t2.small", - "t2.medium", - "t2.large", - "t2.xlarge", - "m4.large", - "m4.xlarge", -] - -# Rule to restrict instance types -instance_type_allowed = rule { - all get_resources("aws_instance") as r { - r.applied.instance_type in allowed_types - } -} - -# Main rule that requires other rules to be true -main = rule { - (instance_type_allowed) else true -} \ No newline at end of file diff --git a/sentinel/aws-restrict-instance-type-stage.sentinel b/sentinel/aws-restrict-instance-type-stage.sentinel deleted file mode 100644 index b1487c2..0000000 --- a/sentinel/aws-restrict-instance-type-stage.sentinel +++ /dev/null @@ -1,62 +0,0 @@ -import "tfplan" - -# Get an array of all resources of the given type (or an empty array). -get_resources = func(type) { - if length(tfplan.module_paths else []) > 0 { # always true in the real tfplan import - return get_resources_all_modules(type) - } else { # fallback for tests - return get_resources_root_only(type) - } -} - -get_resources_root_only = func(type) { - resources = [] - named_and_counted_resources = tfplan.resources[type] else {} - # Get resource bodies out of nested resource maps, from: - # {"name": {"0": {"applied": {...}, "diff": {...} }, "1": {...}}, "name": {...}} - # to: - # [{"applied": {...}, "diff": {...}}, {"applied": {...}, "diff": {...}}, ...] - for named_and_counted_resources as _, instances { - for instances as _, body { - append(resources, body) - } - } - return resources -} - -get_resources_all_modules = func(type) { - resources = [] - for tfplan.module_paths as path { - named_and_counted_resources = tfplan.module(path).resources[type] else {} - # Get resource bodies out of nested resource maps, from: - # {"name": {"0": {"applied": {...}, "diff": {...} }, "1": {...}}, "name": {...}} - # to: - # [{"applied": {...}, "diff": {...}}, {"applied": {...}, "diff": {...}}, ...] - for named_and_counted_resources as _, instances { - for instances as _, body { - append(resources, body) - } - } - } - return resources -} - -# Allowed Types -allowed_types = [ - "t2.small", - "t2.medium", - "t2.large", - "t2.xlarge", -] - -# Rule to restrict instance types -instance_type_allowed = rule { - all get_resources("aws_instance") as r { - r.applied.instance_type in allowed_types - } -} - -# Main rule that requires other rules to be true -main = rule { - (instance_type_allowed) else true -} diff --git a/sentinel/azurerm-block-allow-all-cidr.sentinel b/sentinel/azurerm-block-allow-all-cidr.sentinel deleted file mode 100644 index 070226e..0000000 --- a/sentinel/azurerm-block-allow-all-cidr.sentinel +++ /dev/null @@ -1,59 +0,0 @@ -import "tfplan" - -# Get an array of all resources of the given type (or an empty array). -get_resources = func(type) { - if length(tfplan.module_paths else []) > 0 { # always true in the real tfplan import - return get_resources_all_modules(type) - } else { # fallback for tests - return get_resources_root_only(type) - } -} - -get_resources_root_only = func(type) { - resources = [] - named_and_counted_resources = tfplan.resources[type] else {} - # Get resource bodies out of nested resource maps, from: - # {"name": {"0": {"applied": {...}, "diff": {...} }, "1": {...}}, "name": {...}} - # to: - # [{"applied": {...}, "diff": {...}}, {"applied": {...}, "diff": {...}}, ...] - for named_and_counted_resources as _, instances { - for instances as _, body { - append(resources, body) - } - } - return resources -} - -get_resources_all_modules = func(type) { - resources = [] - for tfplan.module_paths as path { - named_and_counted_resources = tfplan.module(path).resources[type] else {} - # Get resource bodies out of nested resource maps, from: - # {"name": {"0": {"applied": {...}, "diff": {...} }, "1": {...}}, "name": {...}} - # to: - # [{"applied": {...}, "diff": {...}}, {"applied": {...}, "diff": {...}}, ...] - for named_and_counted_resources as _, instances { - for instances as _, body { - append(resources, body) - } - } - } - return resources -} - -disallowed_cidr_blocks = [ - "0.0.0.0/0", - "*", -] - -block_allow_all = rule { - all get_resources("azurerm_network_security_group") as sg { - all sg.applied.security_rule as _, sr { - (sr.source_address_prefix not in disallowed_cidr_blocks) or sr.access == "Deny" - } - } -} - -main = rule { - (block_allow_all) else true -} \ No newline at end of file diff --git a/sentinel/azurerm-restrict-vm-size.sentinel b/sentinel/azurerm-restrict-vm-size.sentinel deleted file mode 100644 index 78bf40a..0000000 --- a/sentinel/azurerm-restrict-vm-size.sentinel +++ /dev/null @@ -1,70 +0,0 @@ -import "tfplan" - -# Get an array of all resources of the given type (or an empty array). -get_resources = func(type) { - if length(tfplan.module_paths else []) > 0 { # always true in the real tfplan import - return get_resources_all_modules(type) - } else { # fallback for tests - return get_resources_root_only(type) - } -} - -get_resources_root_only = func(type) { - resources = [] - named_and_counted_resources = tfplan.resources[type] else {} - # Get resource bodies out of nested resource maps, from: - # {"name": {"0": {"applied": {...}, "diff": {...} }, "1": {...}}, "name": {...}} - # to: - # [{"applied": {...}, "diff": {...}}, {"applied": {...}, "diff": {...}}, ...] - for named_and_counted_resources as _, instances { - for instances as _, body { - append(resources, body) - } - } - return resources -} - -get_resources_all_modules = func(type) { - resources = [] - for tfplan.module_paths as path { - named_and_counted_resources = tfplan.module(path).resources[type] else {} - # Get resource bodies out of nested resource maps, from: - # {"name": {"0": {"applied": {...}, "diff": {...} }, "1": {...}}, "name": {...}} - # to: - # [{"applied": {...}, "diff": {...}}, {"applied": {...}, "diff": {...}}, ...] - for named_and_counted_resources as _, instances { - for instances as _, body { - append(resources, body) - } - } - } - return resources -} - -# comparison is case-sensitive -# so including both cases for "v" -# since we have seen both used -allowed_vm_sizes = [ - "Standard_D1_v2", - "Standard_D1_V2", - "Standard_D2_v2", - "Standard_D2_V2", - "Standard_DS1_v2", - "Standard_DS1_V2", - "Standard_DS2_v2", - "Standard_DS2_V2", - "Standard_A1", - "Standard_A2", - "Standard_D1", - "Standard_D2", -] - -vm_size_allowed = rule { - all get_resources("azurerm_virtual_machine") as r { - r.applied.vm_size in allowed_vm_sizes - } -} - -main = rule { - (vm_size_allowed) else true -} \ No newline at end of file diff --git a/sentinel/change-window-hours.sentinel b/sentinel/change-window-hours.sentinel deleted file mode 100644 index 58309e6..0000000 --- a/sentinel/change-window-hours.sentinel +++ /dev/null @@ -1,17 +0,0 @@ -import "time" - - -# Time rule info -# We expect requests to only happen during work days (0 for Sunday, 6 for Saturday) -workdays = rule { - time.now.weekday == 0 -} - -# We expect requests to only happen during work hours -workhours = rule { - time.now.hour > 1 and time.now.hour < 12 #GMT Time -} - -main = rule { - workdays and workhours -} diff --git a/sentinel/gcp-block-allow-all-cidr.sentinel b/sentinel/gcp-block-allow-all-cidr.sentinel deleted file mode 100644 index 61d6425..0000000 --- a/sentinel/gcp-block-allow-all-cidr.sentinel +++ /dev/null @@ -1,54 +0,0 @@ -import "tfplan" - -# Get an array of all resources of the given type (or an empty array). -get_resources = func(type) { - if length(tfplan.module_paths else []) > 0 { # always true in the real tfplan import - return get_resources_all_modules(type) - } else { # fallback for tests - return get_resources_root_only(type) - } -} - -get_resources_root_only = func(type) { - resources = [] - named_and_counted_resources = tfplan.resources[type] else {} - # Get resource bodies out of nested resource maps, from: - # {"name": {"0": {"applied": {...}, "diff": {...} }, "1": {...}}, "name": {...}} - # to: - # [{"applied": {...}, "diff": {...}}, {"applied": {...}, "diff": {...}}, ...] - for named_and_counted_resources as _, instances { - for instances as _, body { - append(resources, body) - } - } - return resources -} - -get_resources_all_modules = func(type) { - resources = [] - for tfplan.module_paths as path { - named_and_counted_resources = tfplan.module(path).resources[type] else {} - # Get resource bodies out of nested resource maps, from: - # {"name": {"0": {"applied": {...}, "diff": {...} }, "1": {...}}, "name": {...}} - # to: - # [{"applied": {...}, "diff": {...}}, {"applied": {...}, "diff": {...}}, ...] - for named_and_counted_resources as _, instances { - for instances as _, body { - append(resources, body) - } - } - } - return resources -} - -disallowed_cidr_block = "0.0.0.0/0" - -block_allow_all = rule { - all get_resources("google_compute_firewall") as fw { - disallowed_cidr_block not in fw.applied.source_ranges[0] - } -} - -main = rule { - (block_allow_all) else true -} \ No newline at end of file diff --git a/sentinel/gcp-restrict-machine-type.sentinel b/sentinel/gcp-restrict-machine-type.sentinel deleted file mode 100644 index ccb760a..0000000 --- a/sentinel/gcp-restrict-machine-type.sentinel +++ /dev/null @@ -1,58 +0,0 @@ -import "tfplan" - -# Get an array of all resources of the given type (or an empty array). -get_resources = func(type) { - if length(tfplan.module_paths else []) > 0 { # always true in the real tfplan import - return get_resources_all_modules(type) - } else { # fallback for tests - return get_resources_root_only(type) - } -} - -get_resources_root_only = func(type) { - resources = [] - named_and_counted_resources = tfplan.resources[type] else {} - # Get resource bodies out of nested resource maps, from: - # {"name": {"0": {"applied": {...}, "diff": {...} }, "1": {...}}, "name": {...}} - # to: - # [{"applied": {...}, "diff": {...}}, {"applied": {...}, "diff": {...}}, ...] - for named_and_counted_resources as _, instances { - for instances as _, body { - append(resources, body) - } - } - return resources -} - -get_resources_all_modules = func(type) { - resources = [] - for tfplan.module_paths as path { - named_and_counted_resources = tfplan.module(path).resources[type] else {} - # Get resource bodies out of nested resource maps, from: - # {"name": {"0": {"applied": {...}, "diff": {...} }, "1": {...}}, "name": {...}} - # to: - # [{"applied": {...}, "diff": {...}}, {"applied": {...}, "diff": {...}}, ...] - for named_and_counted_resources as _, instances { - for instances as _, body { - append(resources, body) - } - } - } - return resources -} - -allowed_machine_types = [ - "n1-standard-1", - "n1-standard-2", - "n1-standard-4", -] - -machine_type_allowed = rule { - all get_resources("google_compute_instance") as r { - r.applied.machine_type in allowed_machine_types - } -} - -main = rule { - (machine_type_allowed) else true -} \ No newline at end of file diff --git a/sentinel/main.tf b/sentinel/main.tf deleted file mode 100644 index 3c6d2f2..0000000 --- a/sentinel/main.tf +++ /dev/null @@ -1,225 +0,0 @@ -terraform { - backend "remote" { - hostname = "app.terraform.io" - organization = "hashicorp-v2" - } -} - -variable "tfe_token" {} - -variable "tfe_hostname" { - description = "The domain where your TFE is hosted." - default = "app.terraform.io" -} - -variable "tfe_organization" { - description = "The TFE organization to apply your changes to." - default = "example_corp" -} - -variable "self_name" { - default = "sentinel_policies" -} - -variable "use_case_name" { - default = "ExampleTeam" -} - -provider "tfe" { - hostname = "${var.tfe_hostname}" - token = "${var.tfe_token}" - version = "~> 0.6" -} - -data "tfe_workspace_ids" "all" { - names = ["*"] - organization = "${var.tfe_organization}" -} - -locals { - workspaces = "${data.tfe_workspace_ids.all.external_ids}" # map of names to IDs -} - -resource "tfe_policy_set" "global" { - name = "global" - description = "Policies that should be enforced on ALL infrastructure." - organization = "${var.tfe_organization}" - global = true - - policy_ids = [ - "${tfe_sentinel_policy.passthrough.id}", - "${tfe_sentinel_policy.aws-block-allow-all-cidr.id}", - "${tfe_sentinel_policy.aws-restrict-instance-type-default.id}", - ] -} - -resource "tfe_policy_set" "production" { - name = "production" - description = "Policies that should be enforced on production infrastructure." - organization = "${var.tfe_organization}" - - policy_ids = [ - "${tfe_sentinel_policy.aws-restrict-instance-type-prod.id}", - #"${tfe_sentinel_policy.prod-change-window-hours.id}", - ] - - workspace_external_ids = [ - "${local.workspaces["${var.use_case_name}-production"]}", - ] -} - -resource "tfe_policy_set" "development" { - name = "development" - description = "Policies that should be enforced on development or scratch infrastructure." - organization = "${var.tfe_organization}" - - policy_ids = [ - "${tfe_sentinel_policy.aws-restrict-instance-type-dev.id}", - "${tfe_sentinel_policy.allowed-working-hours.id}", - ] - - workspace_external_ids = [ - "${local.workspaces["${var.use_case_name}-development"]}", - ] -} - -resource "tfe_policy_set" "staging" { - name = "staging" - description = "Policies that should be enforced on staging environments." - organization = "${var.tfe_organization}" - - policy_ids = [ - "${tfe_sentinel_policy.aws-restrict-instance-type-stage.id}", - ] - - workspace_external_ids = [ - "${local.workspaces["${var.use_case_name}-staging"]}", - ] -} - -resource "tfe_policy_set" "sentinel" { - name = "sentinel" - description = "Policies that watch the watchman. Enforced only on the workspace that manages policies." - organization = "${var.tfe_organization}" - - policy_ids = [ - "${tfe_sentinel_policy.tfe_policies_only.id}", - ] - - workspace_external_ids = [ - "${local.workspaces["${var.self_name}"]}", - ] -} - -# Test/experimental policies: - -resource "tfe_sentinel_policy" "passthrough" { - name = "passthrough" - description = "Just passing through! Always returns 'true'." - organization = "${var.tfe_organization}" - policy = "${file("./passthrough.sentinel")}" - enforce_mode = "advisory" -} - -# Sentinel management policies: - -resource "tfe_sentinel_policy" "tfe_policies_only" { - name = "tfe_policies_only" - description = "The Terraform config that manages Sentinel policies must not use the authenticated tfe provider to manage non-Sentinel resources." - organization = "${var.tfe_organization}" - policy = "${file("./tfe_policies_only.sentinel")}" - enforce_mode = "hard-mandatory" -} - -# Networking policies: - -resource "tfe_sentinel_policy" "aws-block-allow-all-cidr" { - name = "aws-block-allow-all-cidr" - description = "Avoid nasty firewall mistakes (AWS version)" - organization = "${var.tfe_organization}" - policy = "${file("./aws-block-allow-all-cidr.sentinel")}" - enforce_mode = "hard-mandatory" -} - -resource "tfe_sentinel_policy" "azurerm-block-allow-all-cidr" { - name = "azurerm-block-allow-all-cidr" - description = "Avoid nasty firewall mistakes (Azure version)" - organization = "${var.tfe_organization}" - policy = "${file("./azurerm-block-allow-all-cidr.sentinel")}" - enforce_mode = "hard-mandatory" -} - -resource "tfe_sentinel_policy" "gcp-block-allow-all-cidr" { - name = "gcp-block-allow-all-cidr" - description = "Avoid nasty firewall mistakes (GCP version)" - organization = "${var.tfe_organization}" - policy = "${file("./gcp-block-allow-all-cidr.sentinel")}" - enforce_mode = "hard-mandatory" -} - -# Compute instance policies: - -resource "tfe_sentinel_policy" "aws-restrict-instance-type-dev" { - name = "aws-restrict-instance-type-dev" - description = "Limit AWS instances to approved list (for dev infrastructure)" - organization = "${var.tfe_organization}" - policy = "${file("./aws-restrict-instance-type-dev.sentinel")}" - enforce_mode = "hard-mandatory" -} - -resource "tfe_sentinel_policy" "aws-restrict-instance-type-stage" { - name = "aws-restrict-instance-type-stage" - description = "Limit AWS instances to approved list (for dev infrastructure)" - organization = "${var.tfe_organization}" - policy = "${file("./aws-restrict-instance-type-stage.sentinel")}" - enforce_mode = "soft-mandatory" -} - -resource "tfe_sentinel_policy" "aws-restrict-instance-type-prod" { - name = "aws-restrict-instance-type-prod" - description = "Limit AWS instances to approved list (for prod infrastructure)" - organization = "${var.tfe_organization}" - policy = "${file("./aws-restrict-instance-type-prod.sentinel")}" - enforce_mode = "soft-mandatory" -} - -resource "tfe_sentinel_policy" "aws-restrict-instance-type-default" { - name = "aws-restrict-instance-type-default" - description = "Limit AWS instances to approved list" - organization = "${var.tfe_organization}" - policy = "${file("./aws-restrict-instance-type-default.sentinel")}" - enforce_mode = "soft-mandatory" -} - -resource "tfe_sentinel_policy" "azurerm-restrict-vm-size" { - name = "azurerm-restrict-vm-size" - description = "Limit Azure instances to approved list" - organization = "${var.tfe_organization}" - policy = "${file("./azurerm-restrict-vm-size.sentinel")}" - enforce_mode = "soft-mandatory" -} - -resource "tfe_sentinel_policy" "gcp-restrict-machine-type" { - name = "gcp-restrict-machine-type" - description = "Limit GCP instances to approved list" - organization = "${var.tfe_organization}" - policy = "${file("./gcp-restrict-machine-type.sentinel")}" - enforce_mode = "soft-mandatory" -} - -# General management policies -resource "tfe_sentinel_policy" "allowed-working-hours" { - name = "allowed-working-hours" - description = "Only allow TF applies during specific working hours" - organization = "${var.tfe_organization}" - policy = "${file("./working-hours.sentinel")}" - enforce_mode = "hard-mandatory" -} - -resource "tfe_sentinel_policy" "prod-change-window-hours" { - name = "prod-change-window-hours" - description = "Only allow TF applies to prod environments during change window hours" - organization = "${var.tfe_organization}" - policy = "${file("./change-window-hours.sentinel")}" - enforce_mode = "hard-mandatory" -} diff --git a/sentinel/passthrough.sentinel b/sentinel/passthrough.sentinel deleted file mode 100644 index 07c7c9d..0000000 --- a/sentinel/passthrough.sentinel +++ /dev/null @@ -1,3 +0,0 @@ -main = rule { - true -} \ No newline at end of file diff --git a/sentinel/test/README.md b/sentinel/test/README.md deleted file mode 100644 index 8b13789..0000000 --- a/sentinel/test/README.md +++ /dev/null @@ -1 +0,0 @@ - diff --git a/sentinel/tfe_policies_only.sentinel b/sentinel/tfe_policies_only.sentinel deleted file mode 100644 index a5e2078..0000000 --- a/sentinel/tfe_policies_only.sentinel +++ /dev/null @@ -1,68 +0,0 @@ -import "tfplan" - -# Get an array of all resources of the given type (or an empty array). -get_resources = func(type) { - if length(tfplan.module_paths else []) > 0 { # always true in the real tfplan import - return get_resources_all_modules(type) - } else { # fallback for tests - return get_resources_root_only(type) - } -} - -get_resources_root_only = func(type) { - resources = [] - named_and_counted_resources = tfplan.resources[type] else {} - # Get resource bodies out of nested resource maps, from: - # {"name": {"0": {"applied": {...}, "diff": {...} }, "1": {...}}, "name": {...}} - # to: - # [{"applied": {...}, "diff": {...}}, {"applied": {...}, "diff": {...}}, ...] - for named_and_counted_resources as _, instances { - for instances as _, body { - append(resources, body) - } - } - return resources -} - -get_resources_all_modules = func(type) { - resources = [] - for tfplan.module_paths as path { - named_and_counted_resources = tfplan.module(path).resources[type] else {} - # Get resource bodies out of nested resource maps, from: - # {"name": {"0": {"applied": {...}, "diff": {...} }, "1": {...}}, "name": {...}} - # to: - # [{"applied": {...}, "diff": {...}}, {"applied": {...}, "diff": {...}}, ...] - for named_and_counted_resources as _, instances { - for instances as _, body { - append(resources, body) - } - } - } - return resources -} - -no_tfe_oauth_client = rule { length(get_resources("tfe_oauth_client")) == 0 } -no_tfe_organization = rule { length(get_resources("tfe_organization")) == 0 } -no_tfe_organization_token = rule { length(get_resources("tfe_organization_token")) == 0 } -no_tfe_ssh_key = rule { length(get_resources("tfe_ssh_key")) == 0 } -no_tfe_team = rule { length(get_resources("tfe_team")) == 0 } -no_tfe_team_access = rule { length(get_resources("tfe_team_access")) == 0 } -no_tfe_team_member = rule { length(get_resources("tfe_team_member")) == 0 } -no_tfe_team_members = rule { length(get_resources("tfe_team_members")) == 0 } -no_tfe_team_token = rule { length(get_resources("tfe_team_token")) == 0 } -no_tfe_variable = rule { length(get_resources("tfe_variable")) == 0 } -no_tfe_workspace = rule { length(get_resources("tfe_workspace")) == 0 } - -main = rule { - no_tfe_oauth_client and - no_tfe_organization and - no_tfe_organization_token and - no_tfe_ssh_key and - no_tfe_team and - no_tfe_team_access and - no_tfe_team_member and - no_tfe_team_members and - no_tfe_team_token and - no_tfe_variable and - no_tfe_workspace -} \ No newline at end of file diff --git a/sentinel/working-hours.sentinel b/sentinel/working-hours.sentinel deleted file mode 100644 index fdcca68..0000000 --- a/sentinel/working-hours.sentinel +++ /dev/null @@ -1,17 +0,0 @@ -import "time" - - -# Time rule info -# We expect requests to only happen during work days (0 for Sunday, 6 for Saturday) -workdays = rule { - time.now.weekday > 0 and time.now.weekday < 6 -} - -# We expect requests to only happen during work hours -workhours = rule { - time.now.hour > 12 and time.now.hour < 23 #GMT Time -} - -main = rule { - workdays and workhours -}