{"id":4673,"date":"2025-10-19T13:15:14","date_gmt":"2025-10-19T13:15:14","guid":{"rendered":"https:\/\/geekmungus.co.uk\/?p=4673"},"modified":"2025-10-19T13:15:14","modified_gmt":"2025-10-19T13:15:14","slug":"keeper-using-with-terraform","status":"publish","type":"post","link":"https:\/\/geekmungus.co.uk\/?p=4673","title":{"rendered":"Keeper &#8211; Using with Terraform"},"content":{"rendered":"\n<p>When using with Terraform with Keeper Secrets Manager (KSM) a provider is needed to be configured, the Keeper documentation details how this can be configured.\u00a0<a href=\"https:\/\/docs.keeper.io\/en\/keeperpam\/secrets-manager\/integrations\/terraform\">Keeper: Terraform Provider\u00a0<\/a>Documentation. However, a short version can be found below.<\/p>\n\n\n\n<p><a href=\"https:\/\/docs.keeper.io\/en\/keeperpam\/secrets-manager\/integrations\/terraform\">https:\/\/docs.keeper.io\/en\/keeperpam\/secrets-manager\/integrations\/terraform<\/a><\/p>\n\n\n\n<p>You need to have already created a Keeper Secrets Manager (KSM) profile which you can use for authentication, this you&#8217;ll be exporting to JSON so it can be used by Terraform.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\" id=\"KeeperUsingwithTerraform-ConvertProfilestoTerraformCompatible\">Convert Profiles to Terraform Compatible<\/h1>\n\n\n\n<p>We want to get these Applications (profiles) in a format in which we can use them with Terraform, we can export each of the Applications (profiles) to their own JSON file. The recommendation for using this with Terraform is to use a JSON file.&nbsp;<\/p>\n\n\n\n<p>If your KSM profile is ever updated, you&#8217;ll need to run and update to export an updated JSON file.<\/p>\n\n\n\n<p>Ensure you have entered the Python Virtual Environment where KSM is installed, before you start to administer or run your Terraform:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>source ~\/virtualenvs\/keeper\/bin\/activate<\/code><\/pre>\n\n\n\n<p>Then we can export, in my example I have two KSM profiles which have access to different sets of Secrets, so I&#8217;ll create both of these.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>cd ~\/.keeper\/ksm\/\n\nksm profile export --plain --file-format json ids-imt-net-development > account1-development.json\nksm profile export --plain --file-format json ids-imt-net-staging > account2-staging.json<\/code><\/pre>\n\n\n\n<p>Now fix the permissions on the files:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>chmod 0600 account1-development.json\nchmod 0600 account2-staging.json<\/code><\/pre>\n\n\n\n<p>You now have your JSON files ready for use with Terraform.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\" id=\"KeeperUsingwithTerraform-PrepareTerraformEnvironment\">Prepare Terraform Environment<\/h1>\n\n\n\n<p>We&#8217;re going to show a basic setup of our two Terraform environments which use their own JSON file with the corresponding profile from KSM.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>account1-development.json<\/li>\n\n\n\n<li>account2-staging.json<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"KeeperUsingwithTerraform-Example1-ids-imt-net-development.json\">Example 1 &#8211; account1-development.json<\/h2>\n\n\n\n<p>We add to the providers block the &#8220;secretsmanager&#8221; provider an example is given below (providers.tf):<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>terraform {\n  # Terraform Version\n  required_version = \">= 1.8.2\"\n\n  # Terraform State and Lock Location\n  backend \"s3\" {\n    bucket         = \"account1-development-tgw\"\n    dynamodb_table = \"account1-development-tgw\"\n    key            = \"account1-development-tgw-state\"\n    region         = \"eu-west-2\"\n  }\n\n  # Terraform Providers (with version constraints)\n  required_providers {\n    aws = {\n      source  = \"hashicorp\/aws\"\n      version = \"~>6.11.0\"\n    }\n    secretsmanager = {\n      source  = \"keeper-security\/secretsmanager\"\n      version = \">= 1.0.0\"\n    }\n  }\n}<\/code><\/pre>\n\n\n\n<p>Next we need to specify within the same file (below) the provider &#8220;runtime&#8221;, which specifies where this Terraform environment can find the JSON file containing the credentials to access KSM (Keeper Secrets Manager) to be able to download the secrets we need (using their UID).<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>...\nprovider \"secretsmanager\" {\n  credential = file(\"~\/.keeper\/ksm\/account1-development.json\")\n}<\/code><\/pre>\n\n\n\n<p>Once you have added the new provider (KSM &#8211; secretsmanager) you&#8217;ll likely need to run a &#8220;terraform init&#8221; to load this provider into your Terraform environment.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>terraform init -upgrade<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"975\" height=\"486\" src=\"https:\/\/geekmungus.co.uk\/wp-content\/uploads\/2025\/10\/image-4.png\" alt=\"\" class=\"wp-image-4674\" srcset=\"https:\/\/geekmungus.co.uk\/wp-content\/uploads\/2025\/10\/image-4.png 975w, https:\/\/geekmungus.co.uk\/wp-content\/uploads\/2025\/10\/image-4-300x150.png 300w, https:\/\/geekmungus.co.uk\/wp-content\/uploads\/2025\/10\/image-4-768x383.png 768w\" sizes=\"auto, (max-width: 975px) 100vw, 975px\" \/><\/figure>\n\n\n\n<p>This Terraform environment is now ready to use the variables, let&#8217;s look at a second one.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\" id=\"KeeperUsingwithTerraform-Example2-ids-imt-net-staging.json\">Example 2 &#8211; account2-staging.json<\/h1>\n\n\n\n<p>We add to the providers block the &#8220;secretsmanager&#8221; provider an example is given below (providers.tf):<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>terraform {\n  # Terraform Version\n  required_version = \">= 1.8.2\"\n\n  # Terraform State and Lock Location\n  backend \"s3\" {\n    bucket         = \"account2-staging-tgw\"\n    dynamodb_table = \"account2-staging-tgw\"\n    key            = \"account2-staging-tgw-state\"\n    region         = \"eu-west-2\"\n  }\n\n  # Terraform Providers (with version constraints)\n  required_providers {\n    aws = {\n      source  = \"hashicorp\/aws\"\n      version = \"~>6.11.0\"\n    }\n    secretsmanager = {\n      source  = \"keeper-security\/secretsmanager\"\n      version = \">= 1.0.0\"\n    }\n  }\n}<\/code><\/pre>\n\n\n\n<p>Next we need to specify within the same file (below) the provider &#8220;runtime&#8221;, which specifies where this Terraform environment can find the JSON file containing the credentials to access KSM (Keeper Secrets Manager) to be able to download the secrets we need (using their UID).<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>...\nprovider \"secretsmanager\" {\n  credential = file(\"~\/.config\/ksm\/account2-staging.json\")\n}<\/code><\/pre>\n\n\n\n<p>That&#8217;s it, that&#8217;s both of the Terraform environments ready for use. We can now start retrieving some secrets from KSM within our templates.<\/p>\n\n\n\n<p>Once you have added the new provider (KSM &#8211; secretsmanager) you&#8217;ll likely need to run a &#8220;terraform init&#8221; to load this provider into your Terraform environment.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>terraform init -upgrade<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"975\" height=\"486\" src=\"https:\/\/geekmungus.co.uk\/wp-content\/uploads\/2025\/10\/image-5.png\" alt=\"\" class=\"wp-image-4675\" srcset=\"https:\/\/geekmungus.co.uk\/wp-content\/uploads\/2025\/10\/image-5.png 975w, https:\/\/geekmungus.co.uk\/wp-content\/uploads\/2025\/10\/image-5-300x150.png 300w, https:\/\/geekmungus.co.uk\/wp-content\/uploads\/2025\/10\/image-5-768x383.png 768w\" sizes=\"auto, (max-width: 975px) 100vw, 975px\" \/><\/figure>\n\n\n\n<p>This Terraform environment is now ready to use the variables, let&#8217;s look at a second one.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"KeeperUsingwithTerraform-TerraformExampleusingids-imt-net-development.json\">Terraform Example using account1-development.json<\/h2>\n\n\n\n<p>Now we have the two Terraform environments setup, we can actually start using KSM to obtain the secrets and interpolate them into our Terraform templates at runtime.<\/p>\n\n\n\n<p>We are just going to use a simple example here to obtain one of the shared keys that is stored in KSM for the ids-imt-net-development profile.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"KeeperUsingwithTerraform-GetthePathUIDfortheTunnel1Secret\">Get the Path UID for the Tunnel 1 Secret<\/h2>\n\n\n\n<p>Run the following to show up the secrets:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>(keeper) user@machine:~\/vscode\/aws-transit-gateway-development$ ksm secrets list\n\n UID                     Record Type Title\n ----------------------- ----------- -------------------------------------------------------\n ECxfDDZa123456VorEaQ  login       account2 - AWS VPN Preshared Keys (Tunnel 1)\n _V7SH123456WS6YB5F_w  login       account2 - AWS VPN Preshared Keys (Tunnel 2)<\/code><\/pre>\n\n\n\n<p>We want the first one, the one with the UID of:\u00a0<strong>ECxfDDZa123456VorEaQ<\/strong><\/p>\n\n\n\n<p>Add the data definition which refers to the UID, so that Terraform can access KSM and retrieve the secret within the <strong>variables.tf<\/strong> file:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Keeper Secrets Manager\ndata \"secretsmanager_login\" \"TUNNEL1_PRESHARED_KEY\" {\n  path = \"ECxfDDZa123456VorEaQ\"\n}<\/code><\/pre>\n\n\n\n<p>Now we have the data resource within Terraform, we can use it to populate a value (i.e. interpolate) in the preshared key for the VPN which is stored in that KSM Secret at runtime, for example see the below, on&nbsp;<strong>line 16<\/strong>, the line:<\/p>\n\n\n\n<p><strong>tunnel1_preshared_key = data.secretsmanager_login.TUNNEL1_PRESHARED_KEY.password<\/strong><\/p>\n\n\n\n<p>Here you can see the use of the variable in action, we first refer to it as a&nbsp;<strong>data<\/strong>&nbsp;resource, then the&nbsp;<strong>secretsmanager_login<\/strong>&nbsp;then the name of the variable you defined above, i.e.&nbsp;<strong>TUNNEL1_PRESHARED_KEY<\/strong>&nbsp;and finally the actual attribute where the secret is held, in this example&nbsp;<strong>password<\/strong>.<\/p>\n\n\n\n<p>At the time of writing you can&#8217;t use\/access &#8220;Custom Fields&#8221;, so all values must be stored in the allowed list of attributes:\u00a0<a href=\"https:\/\/registry.terraform.io\/providers\/keeper-security\/secretsmanager\/latest\/docs\/data-sources\/login\">https:\/\/registry.terraform.io\/providers\/keeper-security\/secretsmanager\/latest\/docs\/data-sources\/login<\/a><\/p>\n\n\n\n<p>So an example AWS VPN configuration within Terraform that uses a KSM obtained secret can be seen below:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>resource \"aws_vpn_connection\" \"TGW-VPN-1\" {\n  customer_gateway_id      = aws_customer_gateway.CGW-WTGC-1.id\n  transit_gateway_id       = aws_ec2_transit_gateway.TGW.id\n  type                     = \"ipsec.1\"\n  tunnel_inside_ip_version = \"ipv4\"\n\n  tunnel1_ike_versions = &#91;\"ikev2\"]\n  tunnel2_ike_versions = &#91;\"ikev2\"]\n\n  tunnel1_dpd_timeout_action = \"clear\"\n  tunnel2_dpd_timeout_action = \"clear\"\n\n  tunnel1_dpd_timeout_seconds = 30\n  tunnel2_dpd_timeout_seconds = 30\n\n  tunnel1_preshared_key = data.secretsmanager_login.TUNNEL1_PRESHARED_KEY.password\n  tunnel2_preshared_key = data.secretsmanager_login.TUNNEL2_PRESHARED_KEY.password\n\n  tunnel1_inside_cidr = var.tunnel1_inside_cidr\n  tunnel2_inside_cidr = var.tunnel2_inside_cidr\n\n  # IPSEC Phase1 Settings (IKE)\n  tunnel1_phase1_encryption_algorithms = &#91;\"AES256\"]\n  tunnel2_phase1_encryption_algorithms = &#91;\"AES256\"]\n\n  tunnel1_phase1_integrity_algorithms = &#91;\"SHA2-256\", \"SHA2-512\"]\n  tunnel2_phase1_integrity_algorithms = &#91;\"SHA2-256\", \"SHA2-512\"]\n\n  tunnel1_phase1_dh_group_numbers = &#91;14, 21]\n  tunnel2_phase1_dh_group_numbers = &#91;14, 21]\n\n  tunnel1_phase1_lifetime_seconds = 14400\n  tunnel2_phase1_lifetime_seconds = 14400\n\n  # IPSEC Phase2 Settings\n  tunnel1_phase2_encryption_algorithms = &#91;\"AES256\"]\n  tunnel2_phase2_encryption_algorithms = &#91;\"AES256\"]\n\n  tunnel1_phase2_integrity_algorithms = &#91;\"SHA2-256\", \"SHA2-512\"]\n  tunnel2_phase2_integrity_algorithms = &#91;\"SHA2-256\", \"SHA2-512\"]\n\n  tunnel1_phase2_dh_group_numbers = &#91;14, 21]\n  tunnel2_phase2_dh_group_numbers = &#91;14, 21]\n\n  tunnel1_phase2_lifetime_seconds = 3600\n  tunnel2_phase2_lifetime_seconds = 3600\n\n  tunnel1_log_options {\n    cloudwatch_log_options {\n      log_enabled       = true\n      log_group_arn     = aws_cloudwatch_log_group.VPN_Tunnel1_Log_Group.arn\n      log_output_format = \"text\"\n    }\n  }\n\n  tunnel2_log_options {\n    cloudwatch_log_options {\n      log_enabled       = true\n      log_group_arn     = aws_cloudwatch_log_group.VPN_Tunnel2_Log_Group.arn\n      log_output_format = \"text\"\n    }\n  }\n\n  tags = {\n    name         = \"TGW-VPN-1\",\n    environment  = var.environment\n    wsi_owner    = var.wsi_owner\n    project_code = var.project_code\n    budget_code  = var.budget_code\n    function = \"VPN\"\n  }\n}<\/code><\/pre>\n\n\n\n<p>You can find a list of the different types of value you can store in Keeper Secrets Manager with:\u00a0<a href=\"https:\/\/docs.keeper.io\/en\/keeperpam\/secrets-manager\/integrations\/terraform\">https:\/\/docs.keeper.io\/en\/keeperpam\/secrets-manager\/integrations\/terraform<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>When using with Terraform with Keeper Secrets Manager (KSM) a provider is needed to be configured, the Keeper documentation details how this can be configured.\u00a0Keeper: Terraform Provider\u00a0Documentation. However, a short version can be found below. https:\/\/docs.keeper.io\/en\/keeperpam\/secrets-manager\/integrations\/terraform You need to have already created a Keeper Secrets Manager (KSM) profile which you can use for authentication, this &#8230; <a title=\"Keeper &#8211; Using with Terraform\" class=\"read-more\" href=\"https:\/\/geekmungus.co.uk\/?p=4673\" aria-label=\"Read more about Keeper &#8211; Using with Terraform\">Read more<\/a><\/p>\n","protected":false},"author":4,"featured_media":4327,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[28,45],"tags":[],"class_list":["post-4673","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-aws","category-terraform"],"_links":{"self":[{"href":"https:\/\/geekmungus.co.uk\/index.php?rest_route=\/wp\/v2\/posts\/4673","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/geekmungus.co.uk\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/geekmungus.co.uk\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/geekmungus.co.uk\/index.php?rest_route=\/wp\/v2\/users\/4"}],"replies":[{"embeddable":true,"href":"https:\/\/geekmungus.co.uk\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=4673"}],"version-history":[{"count":2,"href":"https:\/\/geekmungus.co.uk\/index.php?rest_route=\/wp\/v2\/posts\/4673\/revisions"}],"predecessor-version":[{"id":4677,"href":"https:\/\/geekmungus.co.uk\/index.php?rest_route=\/wp\/v2\/posts\/4673\/revisions\/4677"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/geekmungus.co.uk\/index.php?rest_route=\/wp\/v2\/media\/4327"}],"wp:attachment":[{"href":"https:\/\/geekmungus.co.uk\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=4673"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/geekmungus.co.uk\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=4673"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/geekmungus.co.uk\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=4673"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}