Ephemeral blog post (#384)

Signed-off-by: Christian Mesh <christianmesh1@gmail.com>
Signed-off-by: James Humphries <james@james-humphries.co.uk>
Co-authored-by: James Humphries <James@james-humphries.co.uk>
Co-authored-by: Ilia Gogotchuri <ilia.gogotchuri0@gmail.com>
This commit is contained in:
Christian Mesh 2025-09-29 06:25:50 -04:00 committed by GitHub
parent 3f3db1835a
commit c74c653a5f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 101 additions and 88 deletions

View file

@ -19,6 +19,7 @@ module.exports = {
"react/no-unescaped-entities": ["error", { forbid: [">", "}"] }],
"jsx-a11y/click-events-have-key-events": "off",
"jsx-a11y/no-static-element-interactions": "off",
"max-len": ["error", { code: 200 }],
},
overrides: [
{

View file

@ -0,0 +1,49 @@
---
title: Ephemeral Support in OpenTofu
slug: ephemeral-ready-for-testing
---
We are excited to announce the availability of Ephemeral and Write-Only features in the latest nightly builds of OpenTofu!
This is a [highly requested feature](https://github.com/opentofu/opentofu/issues/1996) that we have been hard at work on for a while now. All the necessary components have now been merged and are available in the nightly builds of OpenTofu and will be available in the upcoming 1.11 release.
:::warning
Do not test this release on a production project! It is not a stable release!
:::
## Downloading the nightly builds
The nightly builds are available exclusively from the [OpenTofu Nightly Repository](https://nightlies.opentofu.org/nightlies/). Please choose the select the appropriate file for your platform.
## Ephemeral Overview
A long-standing issue with OpenTofu and its predecessor has been the storage of sensitive data within the state. Prior to Ephemeral, this data contained everything that OpenTofu knows about the resources it manages. This includes sensitive values, keys, and other secrets that could lead to security incidents if leaked. The introduction of Ephemeral and Write-Only allows for careful configuration to prevent storing this secret data, ensuring that these values only exist within the duration of a single execution of the `tofu` binary. It also allows for transient resources, such as network tunnels, to be made available during specific portions of the OpenTofu Plan/Apply flow.
### Prior Solutions
As many seasoned OpenTofu and Terraform authors know, values and attributes can be marked as [Sensitive](/docs/language/functions/sensitive/) to prevent them from being displayed in the CLI. This helps prevent accidental exposure of secrets through CI/CD logs and other mechanisms. It still however stores the plain text data in the state.
To remedy this, OpenTofu introduced [State and Plan Encryption](/docs/language/state/encryption/). This feature allows you to carefully protect your plan and state data, as well as ensuring that they have not been tampered with in transport. Although the values are still stored within the state and plan, this adds an additional level of security to make it much more difficult for attackers to gain access. It protects the entire state and plan, regardless of how individual resources are configured or marked. Therefore it is still recommended to use state and plan encryption, even after adopting Ephemeral concepts into your workflow.
### Ephemeral Resources
[Ephemeral Resources](/docs/main/language/ephemerality/ephemeral-resources/) are entities that only exist during the execution of a single command. They are opened at the beginning of an OpenTofu operation to get their ephemeral value and closed at the end of the operation. These resources can represent anything from keys in a Key Management System to a SSH proxy that is established when the resource is opened.
The attributes of Ephemeral Resources are marked as `ephemeral` and as such can only be used in [very specific contexts](/docs/main/language/ephemerality/ephemeral-resources/).
### Write-Only Attributes
[Write-Only](/docs/main/language/ephemerality/write-only-attributes/) attributes are a special case that has been added to Managed Resources to allow passing ephemeral data into non-ephemeral resources. These attributes can be set in configuration, but their values will never be stored in state or even available as an attribute to be accessed in other portions of the configuration.
This is geared toward sending ephemeral data, such as passwords and keys, into resources which use and/or manage this data outside of OpenTofu.
### Further Reading
This blog post barely scratches the surface of what is now possible with Ephemeral and Write-Only. Peruse our latest docs for a more [complete overview and in-depth examples](/docs/main/language/ephemerality/)!
## Providing feedback
Thank you for taking the time to test this preview release. If you have any feedback, please use [a GitHub issue](https://github.com/opentofu/opentofu/issues/new/choose) or chat with us on the [OpenTofu Slack](https://opentofu.org/slack/).

@ -1 +1 @@
Subproject commit 23d2eafe670bb8c4f57e0749e74be134c9003b0d
Subproject commit 79e5070b47805816e7387b925bcb1a174f58d0b0

View file

@ -1,58 +0,0 @@
import React from "react";
import PatternBg from "@site/src/components/PatternBg";
import Button from "@site/src/components/Button";
import Link from "@docusaurus/Link";
import { PropBlogPostContent } from "@docusaurus/plugin-content-blog";
type BlogLastPostProps = {
item: {
content: PropBlogPostContent;
};
};
export default function BlogLastPost({ item }: BlogLastPostProps) {
const { permalink, title, date, formattedDate, description } =
item.content.metadata;
return (
<div className="pt-8 pb-12 md:pt-12 md:pb-20 flex items-center justify-center">
<PatternBg />
<div className="max-w-7xl mx-auto px-4">
<article>
<div className="flex flex-col md:flex-row gap-6">
<div className="flex-1">
<Link to={permalink}>
<img src={item.content.frontMatter.image} alt={title} />
</Link>
</div>
<div className="flex-1 flex flex-col justify-center items-start">
<time
dateTime={date}
itemProp="datePublished"
className="text-brand-650 dark:text-brand-500 uppercase font-bold"
>
{formattedDate}
</time>
<h3 className="leading-snug text-3xl font-bold my-2 line-clamp-5">
<Link
to={permalink}
className="text-gray-900 dark:text-gray-50 hover:text-brand-650 dark:hover:text-brand-500"
>
{title}
</Link>
</h3>
<p className="text-gray-600 dark:text-gray-500 mb-4 line-clamp-3">
{description}
</p>
<Button variant="secondary" href={permalink}>
<span aria-hidden>Read More</span>
<span className="sr-only">Read more about: ${title}</span>
</Button>
</div>
</div>
</article>
</div>
</div>
);
}

View file

@ -1,5 +1,4 @@
import Link from "@docusaurus/Link";
import Button from "@site/src/components/Button";
import React from "react";
import { PropBlogPostContent } from "@docusaurus/plugin-content-blog";
@ -7,29 +6,35 @@ type BlogListItemProps = {
item: {
content: PropBlogPostContent;
};
isLatestPost?: boolean;
};
export default function BlogListItem({ item }: BlogListItemProps) {
export default function BlogListItem({
item,
isLatestPost: isLatest = false,
}: BlogListItemProps) {
const { permalink, title, date, formattedDate, description } =
item.content.metadata;
return (
<div className="flex flex-col gap-6">
<div className="flex gap-6">
<div className="flex-1">
<Link to={permalink}>
<img src={item.content.frontMatter.image} alt={title} />
</Link>
</div>
<div className="flex-1 flex flex-col justify-center items-start">
<time
dateTime={date}
itemProp="datePublished"
className="text-brand-650 dark:text-brand-500 uppercase font-bold"
>
{formattedDate}
</time>
<div className="flex items-center gap-3 mb-2">
<time
dateTime={date}
itemProp="datePublished"
className="text-brand-650 dark:text-brand-500 uppercase font-bold"
>
{formattedDate}
</time>
{isLatest && (
<span className="inline-flex px-3 py-1 text-sm font-medium rounded-full bg-blue-600/90 text-white dark:bg-blue-500/90">
Latest
</span>
)}
</div>
<h3 className="leading-snug text-xl font-bold my-2 line-clamp-3">
<h3 className="leading-snug text-xl font-bold mt-2 mb-2 line-clamp-3">
<Link
to={permalink}
className="text-gray-900 dark:text-gray-50 hover:text-brand-650 dark:hover:text-brand-500"
@ -40,11 +45,24 @@ export default function BlogListItem({ item }: BlogListItemProps) {
<p className="text-gray-600 dark:text-gray-500 mb-4 line-clamp-3">
{description}
</p>
<Button variant="secondary" href={permalink}>
<span aria-hidden>Read More</span>
<span className="sr-only">Read more about: ${title}</span>
</Button>
<Link
to={permalink}
className="text-brand-650 dark:text-brand-500 hover:text-brand-700 dark:hover:text-brand-400 text-sm font-medium"
>
Read More
</Link>
</div>
{item.content.frontMatter.image && (
<div className="w-32 flex items-center">
<Link to={permalink} className="block w-full">
<img
src={item.content.frontMatter.image}
alt={title}
className="w-full h-auto object-cover rounded"
/>
</Link>
</div>
)}
</div>
);
}

View file

@ -11,9 +11,13 @@ type BlogListItemsProps = {
export default function BlogListItems({ items }: BlogListItemsProps) {
return (
<div className="max-w-7xl mx-auto grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-x-6 gap-y-12 py-4 md:py-10 px-4">
{items.map((item) => (
<BlogListItem item={item} key={item.content.metadata.permalink} />
<div className="max-w-7xl mx-auto space-y-8 px-4">
{items.map((item, index) => (
<BlogListItem
item={item}
key={item.content.metadata.permalink}
isLatestPost={index === 0}
/>
))}
</div>
);

View file

@ -5,23 +5,20 @@ import BlogLayout from "@theme/BlogLayout";
import BlogListPaginator from "@theme/BlogListPaginator";
import SearchMetadata from "@theme/SearchMetadata";
import BlogListItems from "@theme/BlogListItems";
import BlogLastPost from "@theme/BlogLastPost";
import { Props } from "@theme/BlogListPage";
import PatternBg from "@site/src/components/PatternBg";
export default function BlogListPage(props: Props) {
const { metadata, items } = props;
const { blogDescription, blogTitle } = metadata;
const lastPost = items[0];
const otherPosts = items.slice(1);
return (
<>
<PageMetadata title={blogTitle} description={blogDescription} />
<SearchMetadata tag="blog_posts_list" />
<PatternBg />
<BlogLayout>
<BlogLastPost item={lastPost} />
<BlogListItems items={otherPosts} />
<BlogListItems items={items} />
<BlogListPaginator metadata={metadata} />
</BlogLayout>
</>

View file

@ -74,9 +74,11 @@ export default function BlogPostItem({ children }: Props) {
<BlogPostItemContainer>
<BlogPostItemHeader />
{ frontMatter.image &&
<div className="max-w-5xl mx-auto flex justify-center">
<img src={frontMatter.image} alt={title} />
</div>
}
<div className="max-w-5xl mx-auto flex flex-col justify-center relative">
<div className="lg:sticky top-0">
<div className="flex justify-center mb-4 md:mb-10 lg:flex-col relative lg:absolute right-0 top-4 md:top-10 gap-4">