<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Chris Gradwohl</title>
    <description>The latest articles on DEV Community by Chris Gradwohl (@chris_gradwohl).</description>
    <link>https://dev.to/chris_gradwohl</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1931531%2F0b3096ff-f527-4f9e-a00c-159100807ca6.png</url>
      <title>DEV Community: Chris Gradwohl</title>
      <link>https://dev.to/chris_gradwohl</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/chris_gradwohl"/>
    <language>en</language>
    <item>
      <title>Straight Forward Tofu</title>
      <dc:creator>Chris Gradwohl</dc:creator>
      <pubDate>Thu, 19 Sep 2024 14:27:35 +0000</pubDate>
      <link>https://dev.to/aws-builders/straight-forward-tofu-594c</link>
      <guid>https://dev.to/aws-builders/straight-forward-tofu-594c</guid>
      <description>&lt;p&gt;In this entry level blog post, we will learn the basics of provisioning infrastructure with OpenTofu by building an application logging service using AWS EventBridge and AWS CloudWatch.&lt;/p&gt;

&lt;p&gt;Here is a high level architecture diagram of what we will build in this blog post. Let's dive right in!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxrlxqpf2srqp3gdo9qjy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxrlxqpf2srqp3gdo9qjy.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Repo Link
&lt;/h2&gt;

&lt;p&gt;If you want to skip to the code, checkout the repo here: &lt;a href="https://github.com/cloudspark-io/tofu_log_service" rel="noopener noreferrer"&gt;https://github.com/cloudspark-io/tofu_log_service&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;To follow along in this post, you will need an AWS Account and the AWS CLI configured.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let's Go!
&lt;/h2&gt;

&lt;p&gt;Head over to &lt;a href="https://opentofu.org/docs/intro/install/" rel="noopener noreferrer"&gt;https://opentofu.org/docs/intro/install/&lt;/a&gt; and install OpenTofu for your operating system.&lt;/p&gt;

&lt;p&gt;For Mac:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

brew update

brew &lt;span class="nb"&gt;install &lt;/span&gt;opentofu

tofu &lt;span class="nt"&gt;--version&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Let's setup a repo for our project.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="nb"&gt;mkdir &lt;/span&gt;tofu_log_service &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;

&lt;span class="nb"&gt;cd &lt;/span&gt;tofu_log_service &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;

&lt;span class="nb"&gt;touch &lt;/span&gt;main.tf &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;

git init &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;

git add &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;

git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"hello tofu!"&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Configure the Provider
&lt;/h2&gt;

&lt;p&gt;OpenTofu, like Terraform, relies on software plugins called &lt;strong&gt;providers&lt;/strong&gt; to interact with cloud providers, SaaS providers, and other APIs. Each provider adds a set of &lt;strong&gt;resource&lt;/strong&gt; and &lt;strong&gt;data&lt;/strong&gt; blocks that we can use to provision our infrastructure.&lt;/p&gt;

&lt;p&gt;The first thing we need to do is configure Tofu to use the AWS provider. To do that, we still need to use the &lt;strong&gt;&lt;code&gt;terraform&lt;/code&gt;&lt;/strong&gt; block and reference the &lt;strong&gt;&lt;code&gt;hashicorp/aws&lt;/code&gt;&lt;/strong&gt; provider, even though we are using the OpenTofu language. OpenTofu has kept these naming conventions so that it can &lt;em&gt;remain backward compatible&lt;/em&gt; with existing Terraform projects. The provider source code is actually owned and hosted on the OpenTofu &lt;a href="https://search.opentofu.org/" rel="noopener noreferrer"&gt;registry&lt;/a&gt;, making OpenTofu truly backwards compatible &lt;em&gt;and&lt;/em&gt; open source.&lt;/p&gt;

&lt;p&gt;Open &lt;code&gt;main.tf&lt;/code&gt; and add the following to configure Tofu to use the AWS provider.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;

&lt;span class="k"&gt;terraform&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;required_providers&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;aws&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;source&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"hashicorp/aws"&lt;/span&gt;
      &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"~&amp;gt; 5.0"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nx"&gt;required_version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&amp;gt;= 1.8.0"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;"aws"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;region&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"us-west-1"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Next let's learn how to add infrastructure resources to our Tofu configuration.&lt;/p&gt;

&lt;h2&gt;
  
  
  The &lt;strong&gt;&lt;code&gt;resource&lt;/code&gt;&lt;/strong&gt; Block
&lt;/h2&gt;

&lt;p&gt;To create and configure an infrastructure resources we use the &lt;strong&gt;&lt;code&gt;resource&lt;/code&gt;&lt;/strong&gt; block. We write a collection of &lt;strong&gt;&lt;code&gt;resource&lt;/code&gt;&lt;/strong&gt; blocks to describe one or more infrastructure objects, such as a Lambda function, an EC2 instance, or a higher-level module such an EKS Cluster.&lt;/p&gt;

&lt;p&gt;All &lt;strong&gt;&lt;code&gt;resource&lt;/code&gt;&lt;/strong&gt; declarations use the following syntax:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;

&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"resource_type"&lt;/span&gt; &lt;span class="s2"&gt;"local_name"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="c1"&gt;# Configuration block&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;resource_type&lt;/code&gt;:&lt;/strong&gt; Specifies the type of resource you want to manage (e.g., &lt;code&gt;aws_instance&lt;/code&gt;, &lt;code&gt;aws_s3_bucket&lt;/code&gt;). It defined by the AWS provider we configured a moment ago in the the ‘terraform’ block.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;local_name&lt;/code&gt;:&lt;/strong&gt; A unique identifier within the Tofu configuration for this resource. The &lt;strong&gt;&lt;code&gt;local_name&lt;/code&gt;&lt;/strong&gt; is used to refer to this resource from elsewhere in the same Tofu project, but has no significance outside that module's scope.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The &lt;strong&gt;&lt;code&gt;resource_type&lt;/code&gt;&lt;/strong&gt; and &lt;strong&gt;&lt;code&gt;local_name&lt;/code&gt;&lt;/strong&gt; together serve as an identifier for a given resource and so must be unique within a module.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The &lt;strong&gt;&lt;code&gt;Configuration block&lt;/code&gt;&lt;/strong&gt; defines the specific resources settings and properties. It contains key-value pairs that specify the desired state or attributes of the resource being created.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Creating EventBridge Resources
&lt;/h2&gt;

&lt;p&gt;Let's create an EventBridge rule for our logging service. This resource will define the API of our service. Add the following to &lt;code&gt;main.tf&lt;/code&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;

&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_cloudwatch_event_rule"&lt;/span&gt; &lt;span class="s2"&gt;"service_log_rule"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
  &lt;span class="nx"&gt;name&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"service-log-rule"&lt;/span&gt;  
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"EventBridge rule to capture logs from any application service and send to CloudWatch Log Group."&lt;/span&gt;  

  &lt;span class="nx"&gt;event_pattern&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jsonencode&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;  
    &lt;span class="s2"&gt;"detail"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
      &lt;span class="s2"&gt;"env"&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="s2"&gt;"wildcard"&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"*"&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;  
      &lt;span class="s2"&gt;"level"&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"info"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"warn"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;  
      &lt;span class="s2"&gt;"message"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="s2"&gt;"wildcard"&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"*"&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;  
      &lt;span class="s2"&gt;"service"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="s2"&gt;"wildcard"&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"*"&lt;/span&gt; &lt;span class="p"&gt;}]&lt;/span&gt;  
    &lt;span class="p"&gt;},&lt;/span&gt;  
    &lt;span class="s2"&gt;"detail-type"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"service.log"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;  
    &lt;span class="s2"&gt;"source"&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="s2"&gt;"wildcard"&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"*"&lt;/span&gt; &lt;span class="p"&gt;}]&lt;/span&gt;  
  &lt;span class="p"&gt;})&lt;/span&gt;  
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This &lt;strong&gt;&lt;code&gt;resource&lt;/code&gt;&lt;/strong&gt; block defines an &lt;strong&gt;&lt;code&gt;aws_cloudwatch_event_rule&lt;/code&gt;&lt;/strong&gt; which defines the properties of the log events that our service will be listening for. All events emitted to EventBridge that match this event schema will be forwarded to our CloudWatch log group.&lt;/p&gt;

&lt;p&gt;To learn more about how EventBridge filters events check out this documentation: &lt;a href="https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-events.html" rel="noopener noreferrer"&gt;https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-events.html&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating CloudWatch Resources
&lt;/h2&gt;

&lt;p&gt;Next, let's create our CloudWatch resources, which will serve as the destination for our log events.&lt;/p&gt;

&lt;p&gt;Add the following to &lt;strong&gt;&lt;code&gt;main.tf&lt;/code&gt;&lt;/strong&gt; &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;

&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_cloudwatch_log_group"&lt;/span&gt; &lt;span class="s2"&gt;"log_service_cw_log_group"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"/aws/events/log-service"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_cloudwatch_event_target"&lt;/span&gt; &lt;span class="s2"&gt;"services_event_target"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;rule&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_cloudwatch_event_rule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;service_log_rule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;
  &lt;span class="nx"&gt;arn&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_cloudwatch_log_group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log_service_cw_log_group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arn&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The first &lt;strong&gt;&lt;code&gt;resource&lt;/code&gt;&lt;/strong&gt; block defines an &lt;strong&gt;&lt;code&gt;aws_cloudwatch_log_group&lt;/code&gt;&lt;/strong&gt; to store our incoming logs.&lt;/p&gt;

&lt;p&gt;The second &lt;strong&gt;&lt;code&gt;resource&lt;/code&gt;&lt;/strong&gt; block defines an &lt;strong&gt;&lt;code&gt;aws_cloudwatch_event_target&lt;/code&gt;&lt;/strong&gt; which maps the EventBridge rule we defined earlier  to the CloudWatch Group. &lt;/p&gt;

&lt;p&gt;Note how we use the unique identifier of our EventBridge rule, consisting of the resource_name and local_name, to pass the &lt;code&gt;name&lt;/code&gt; value to the CloudWatch configuration.&lt;/p&gt;

&lt;p&gt;The last thing we need to do is to give EventBridge permissions to write into our new log group. One way we can do that is to create a resource based policy for our CloudWatch Group. Let's review the &lt;strong&gt;&lt;code&gt;data&lt;/code&gt;&lt;/strong&gt; block in OpenTofu and see how it can simplify writing this IAM policy.&lt;/p&gt;

&lt;h2&gt;
  
  
  ## The &lt;strong&gt;&lt;code&gt;data&lt;/code&gt;&lt;/strong&gt; Block
&lt;/h2&gt;

&lt;p&gt;Data sources in OpenTofu allow us to reference or create information from &lt;strong&gt;&lt;em&gt;external&lt;/em&gt;&lt;/strong&gt; resources that  aren’t available to reference otherwise. &lt;/p&gt;

&lt;p&gt;All &lt;strong&gt;&lt;code&gt;data&lt;/code&gt;&lt;/strong&gt; block declarations use the following syntax:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;

&lt;span class="k"&gt;data&lt;/span&gt; &lt;span class="s2"&gt;"source_type"&lt;/span&gt; &lt;span class="s2"&gt;"local_name"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="c1"&gt;# Configuration block&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;source_type&lt;/code&gt;:&lt;/strong&gt; Specifies the type of data you want to create or reference from the data source. Just like &lt;code&gt;**resource_types**&lt;/code&gt;, it is defined by the AWS provider from OpenTofu.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;local_name&lt;/code&gt;:&lt;/strong&gt; A unique identifier within the Tofu configuration for this data source. The &lt;strong&gt;&lt;code&gt;local_name&lt;/code&gt;&lt;/strong&gt; is used to refer to this data source from elsewhere in the Tofu project.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The &lt;strong&gt;&lt;code&gt;source_type&lt;/code&gt;&lt;/strong&gt; and &lt;strong&gt;&lt;code&gt;local_name&lt;/code&gt;&lt;/strong&gt; together serve as an identifier for a given data source and so must be unique within the Tofu project.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The &lt;strong&gt;&lt;code&gt;Configuration block&lt;/code&gt;&lt;/strong&gt; specifies identifier information for the data lookup or creation. It contains key-value pairs that specify the desired state or attributes of the resource being managed.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now let’s get back to that IAM policy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating IAM Resources
&lt;/h2&gt;

&lt;p&gt;Add the following to your &lt;code&gt;main.tf&lt;/code&gt; file.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;

&lt;span class="k"&gt;data&lt;/span&gt; &lt;span class="s2"&gt;"aws_caller_identity"&lt;/span&gt; &lt;span class="s2"&gt;"current"&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="k"&gt;data&lt;/span&gt; &lt;span class="s2"&gt;"aws_iam_policy_document"&lt;/span&gt; &lt;span class="s2"&gt;"cw_log_group_policy"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
  &lt;span class="nx"&gt;statement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
    &lt;span class="nx"&gt;actions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;  
      &lt;span class="s2"&gt;"logs:CreateLogStream"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
      &lt;span class="s2"&gt;"logs:PutLogEvents"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
    &lt;span class="p"&gt;]&lt;/span&gt;  
    &lt;span class="nx"&gt;resources&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;  
      &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;aws_cloudwatch_log_group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log_service_cw_log_group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arn&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:*"&lt;/span&gt;  
    &lt;span class="p"&gt;]&lt;/span&gt;  
    &lt;span class="nx"&gt;principals&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;# the identity of the principal that is enabled to put logs to this account.  &lt;/span&gt;
      &lt;span class="nx"&gt;identifiers&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"events.amazonaws.com"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;  
      &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Service"&lt;/span&gt;  
    &lt;span class="p"&gt;}&lt;/span&gt;  
  &lt;span class="p"&gt;}&lt;/span&gt;  
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The first &lt;strong&gt;&lt;code&gt;data&lt;/code&gt;&lt;/strong&gt; block defines an &lt;strong&gt;&lt;code&gt;aws_caller_identity&lt;/code&gt;&lt;/strong&gt; and allows us to reference information about the AWS Account that Tofu is currently configured in. We can &lt;em&gt;fetch&lt;/em&gt; information like the Account ID, as well as User IDs and ARNs.&lt;/p&gt;

&lt;p&gt;The second &lt;strong&gt;&lt;code&gt;data&lt;/code&gt;&lt;/strong&gt; block defines an &lt;strong&gt;&lt;code&gt;aws_iam_policy_document&lt;/code&gt;&lt;/strong&gt; and allows us to &lt;em&gt;create&lt;/em&gt; an IAM policy document in JSON format.&lt;/p&gt;

&lt;p&gt;With the help of these &lt;strong&gt;&lt;code&gt;data&lt;/code&gt;&lt;/strong&gt; blocks, it now becomes very easy to create our CloudWatch resource policy. Add the following &lt;strong&gt;&lt;code&gt;resource&lt;/code&gt;&lt;/strong&gt; block to your &lt;code&gt;main.tf&lt;/code&gt; file after the two &lt;strong&gt;&lt;code&gt;data&lt;/code&gt;&lt;/strong&gt; blocks.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;

&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_cloudwatch_log_resource_policy"&lt;/span&gt; &lt;span class="s2"&gt;"eventbridge_log_policy"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
  &lt;span class="nx"&gt;policy_document&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_iam_policy_document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cw_log_group_policy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;
  &lt;span class="nx"&gt;policy_name&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"eventbridge-log-policy"&lt;/span&gt;  
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Note we could have manually written the resource policy, but the &lt;code&gt;aws_iam_policy_document&lt;/code&gt; &lt;strong&gt;&lt;code&gt;data&lt;/code&gt;&lt;/strong&gt; block makes it easier to create a well formatted JSON object that the policy expects. Nice!&lt;/p&gt;

&lt;h2&gt;
  
  
  Initialize Tofu Backend
&lt;/h2&gt;

&lt;p&gt;So far our Tofu project has been configured to use the AWS provider, and we have created all the necessary resources for our logging service.&lt;/p&gt;

&lt;p&gt;Now let's learn how to initialize and deploy the Tofu project.&lt;/p&gt;

&lt;p&gt;Run &lt;strong&gt;&lt;code&gt;tofu init&lt;/code&gt;&lt;/strong&gt; and you should see the following output:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

Initializing the backend...

Initializing provider plugins...

OpenTofu has been successfully initialized!

You may now begin working with OpenTofu. Try running "tofu plan" to see
any changes that are required for your infrastructure. All OpenTofu commands
should now work.

If you ever set or change modules or backend configuration for OpenTofu,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;You should also see a &lt;code&gt;.terraform/&lt;/code&gt; folder and &lt;code&gt;.terraform.lock.hcl&lt;/code&gt; file now in your repository. These assets hold and describe all the Tofu dependencies required for the AWS provider packages.&lt;/p&gt;

&lt;p&gt;After initializing the Tofu project with &lt;code&gt;tofu init&lt;/code&gt; we are ready to deploy the infrastructure to our AWS account.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tofu Validate, Plan and Apply
&lt;/h2&gt;

&lt;p&gt;First let's validate our configuration to make sure we don't have any syntax errors. Run &lt;code&gt;tofu validate&lt;/code&gt;. You should see the following output&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

❯ tofu validate
Success! The configuration is valid.


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Before actually deploying the infrastructure to our account, we can view the deployment plan. The deployment plan, gives us an overview about what has changed in our current configuration state, such as what resources have been added, mutated or removed.&lt;/p&gt;

&lt;p&gt;Run &lt;code&gt;tofu plan&lt;/code&gt; to see the deployment plan.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;

&lt;span class="err"&gt;❯&lt;/span&gt; &lt;span class="nx"&gt;tofu&lt;/span&gt; &lt;span class="nx"&gt;plan&lt;/span&gt;
&lt;span class="k"&gt;data&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_caller_identity&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Reading&lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;data&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_caller_identity&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Read&lt;/span&gt; &lt;span class="nx"&gt;complete&lt;/span&gt; &lt;span class="nx"&gt;after&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="err"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;956613775090&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="nx"&gt;OpenTofu&lt;/span&gt; &lt;span class="nx"&gt;used&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;selected&lt;/span&gt; &lt;span class="nx"&gt;providers&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;generate&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;following&lt;/span&gt; &lt;span class="nx"&gt;execution&lt;/span&gt; &lt;span class="nx"&gt;plan&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt; &lt;span class="nx"&gt;Resource&lt;/span&gt; &lt;span class="nx"&gt;actions&lt;/span&gt; &lt;span class="nx"&gt;are&lt;/span&gt; &lt;span class="nx"&gt;indicated&lt;/span&gt; &lt;span class="nx"&gt;with&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;following&lt;/span&gt; &lt;span class="nx"&gt;symbols&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
  &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;create&lt;/span&gt;
 &lt;span class="err"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nx"&gt;read&lt;/span&gt; &lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="k"&gt;data&lt;/span&gt; &lt;span class="nx"&gt;resources&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;

&lt;span class="nx"&gt;OpenTofu&lt;/span&gt; &lt;span class="nx"&gt;will&lt;/span&gt; &lt;span class="nx"&gt;perform&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;following&lt;/span&gt; &lt;span class="nx"&gt;actions&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;

  &lt;span class="c1"&gt;# data.aws_iam_policy_document.cw_log_group_policy will be read during apply&lt;/span&gt;
  &lt;span class="c1"&gt;# (config refers to values not yet known)&lt;/span&gt;
 &lt;span class="err"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="k"&gt;data&lt;/span&gt; &lt;span class="s2"&gt;"aws_iam_policy_document"&lt;/span&gt; &lt;span class="s2"&gt;"cw_log_group_policy"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;            &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;known&lt;/span&gt; &lt;span class="nx"&gt;after&lt;/span&gt; &lt;span class="nx"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;json&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;known&lt;/span&gt; &lt;span class="nx"&gt;after&lt;/span&gt; &lt;span class="nx"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;minified_json&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;known&lt;/span&gt; &lt;span class="nx"&gt;after&lt;/span&gt; &lt;span class="nx"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

      &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;statement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;actions&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
              &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;"logs:CreateLogStream"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;"logs:PutLogEvents"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;]&lt;/span&gt;
          &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;resources&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
              &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;known&lt;/span&gt; &lt;span class="nx"&gt;after&lt;/span&gt; &lt;span class="nx"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="p"&gt;]&lt;/span&gt;

          &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;principals&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;identifiers&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                  &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;"events.amazonaws.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="p"&gt;]&lt;/span&gt;
              &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Service"&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;# aws_cloudwatch_event_rule.service_log_rule will be created&lt;/span&gt;
  &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_cloudwatch_event_rule"&lt;/span&gt; &lt;span class="s2"&gt;"service_log_rule"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;arn&lt;/span&gt;            &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;known&lt;/span&gt; &lt;span class="nx"&gt;after&lt;/span&gt; &lt;span class="nx"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;description&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"EventBridge rule to capture logs from any application service and send to CloudWatch Log Group."&lt;/span&gt;
      &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;event_bus_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"default"&lt;/span&gt;
      &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;event_pattern&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jsonencode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;detail&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                  &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;env&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                      &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                          &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;wildcard&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"*"&lt;/span&gt;
                        &lt;span class="p"&gt;},&lt;/span&gt;
                    &lt;span class="p"&gt;]&lt;/span&gt;
                  &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;level&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                      &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;"info"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                      &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;"warn"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                      &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="p"&gt;]&lt;/span&gt;
                  &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                      &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                          &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;wildcard&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"*"&lt;/span&gt;
                        &lt;span class="p"&gt;},&lt;/span&gt;
                    &lt;span class="p"&gt;]&lt;/span&gt;
                  &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;service&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                      &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                          &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;wildcard&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"*"&lt;/span&gt;
                        &lt;span class="p"&gt;},&lt;/span&gt;
                    &lt;span class="p"&gt;]&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
              &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;detail-type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                  &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;"service.log"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="p"&gt;]&lt;/span&gt;
              &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;source&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                  &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                      &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;wildcard&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"*"&lt;/span&gt;
                    &lt;span class="p"&gt;},&lt;/span&gt;
                &lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;force_destroy&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
      &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;             &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;known&lt;/span&gt; &lt;span class="nx"&gt;after&lt;/span&gt; &lt;span class="nx"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;           &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"service-log-rule"&lt;/span&gt;
      &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;name_prefix&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;known&lt;/span&gt; &lt;span class="nx"&gt;after&lt;/span&gt; &lt;span class="nx"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;tags_all&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;known&lt;/span&gt; &lt;span class="nx"&gt;after&lt;/span&gt; &lt;span class="nx"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;# aws_cloudwatch_event_target.services_event_target will be created&lt;/span&gt;
  &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_cloudwatch_event_target"&lt;/span&gt; &lt;span class="s2"&gt;"services_event_target"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;arn&lt;/span&gt;            &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;known&lt;/span&gt; &lt;span class="nx"&gt;after&lt;/span&gt; &lt;span class="nx"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;event_bus_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"default"&lt;/span&gt;
      &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;force_destroy&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
      &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;             &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;known&lt;/span&gt; &lt;span class="nx"&gt;after&lt;/span&gt; &lt;span class="nx"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;rule&lt;/span&gt;           &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"service-log-rule"&lt;/span&gt;
      &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;target_id&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;known&lt;/span&gt; &lt;span class="nx"&gt;after&lt;/span&gt; &lt;span class="nx"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;# aws_cloudwatch_log_group.log_service_cw_log_group will be created&lt;/span&gt;
  &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_cloudwatch_log_group"&lt;/span&gt; &lt;span class="s2"&gt;"log_service_cw_log_group"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;arn&lt;/span&gt;               &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;known&lt;/span&gt; &lt;span class="nx"&gt;after&lt;/span&gt; &lt;span class="nx"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;                &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;known&lt;/span&gt; &lt;span class="nx"&gt;after&lt;/span&gt; &lt;span class="nx"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;log_group_class&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;known&lt;/span&gt; &lt;span class="nx"&gt;after&lt;/span&gt; &lt;span class="nx"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;              &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"/aws/events/log-service"&lt;/span&gt;
      &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;name_prefix&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;known&lt;/span&gt; &lt;span class="nx"&gt;after&lt;/span&gt; &lt;span class="nx"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;retention_in_days&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
      &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;skip_destroy&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
      &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;tags_all&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;known&lt;/span&gt; &lt;span class="nx"&gt;after&lt;/span&gt; &lt;span class="nx"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;# aws_cloudwatch_log_resource_policy.eventbridge_log_policy will be created&lt;/span&gt;
  &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_cloudwatch_log_resource_policy"&lt;/span&gt; &lt;span class="s2"&gt;"eventbridge_log_policy"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;              &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;known&lt;/span&gt; &lt;span class="nx"&gt;after&lt;/span&gt; &lt;span class="nx"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;policy_document&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;known&lt;/span&gt; &lt;span class="nx"&gt;after&lt;/span&gt; &lt;span class="nx"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;policy_name&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"eventbridge-log-policy"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;Plan&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;change&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;destroy&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;

&lt;span class="err"&gt;─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────&lt;/span&gt;

&lt;span class="nx"&gt;Note&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;You&lt;/span&gt; &lt;span class="nx"&gt;didn&lt;/span&gt;&lt;span class="s1"&gt;'t use the -out option to save this plan, so OpenTofu can'&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt; &lt;span class="nx"&gt;guarantee&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;take&lt;/span&gt; &lt;span class="nx"&gt;exactly&lt;/span&gt; &lt;span class="nx"&gt;these&lt;/span&gt; &lt;span class="nx"&gt;actions&lt;/span&gt; &lt;span class="nx"&gt;if&lt;/span&gt; &lt;span class="nx"&gt;you&lt;/span&gt; &lt;span class="nx"&gt;run&lt;/span&gt; &lt;span class="s2"&gt;"tofu apply"&lt;/span&gt; &lt;span class="nx"&gt;now&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;As you can see, the deployment plan reveals a tree like structure, illustrating the new resources we intend to create as well as summary which states: &lt;code&gt;Plan: 4 to add, 0 to change, 0 to destroy.&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That looks correct to me! Let's deploy our logging service!&lt;/p&gt;

&lt;p&gt;To deploy your infrastructure to your AWS account run &lt;code&gt;tofu apply&lt;/code&gt;. You should see the same deployment plan report we saw earlier. Enter &lt;code&gt;yes&lt;/code&gt; when prompted to start the deployment process.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

Apply complete! Resources: 4 added, 0 changed, 0 destroyed.


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;If you can see a similar message in your terminal, then congrats! Our logging service is now live and ready to use.&lt;/p&gt;

&lt;h2&gt;
  
  
  Verify The Infrastructure
&lt;/h2&gt;

&lt;p&gt;Let's run a quick manual test to ensure that things are working as expected.&lt;/p&gt;

&lt;p&gt;We can use the AWS CLI to mimic one of our service's that will emit events to our logging service.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

aws events put-events  &lt;span class="nt"&gt;--region&lt;/span&gt; us-west-1  &lt;span class="nt"&gt;--entries&lt;/span&gt; &lt;span class="s1"&gt;'[
    {
      "Source": "service.logging",
      "DetailType": "service.log",
      "Detail": "{\"service\": \"example-service\", \"env\": \"production\", \"level\": \"info\", \"message\": \"This is a test log message.\"}",
      "EventBusName": "default"
    }
  ]'&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Finally navigate to the AWS CloudWatch console. Click on log groups and find the log group named &lt;code&gt;/aws/events/log-service&lt;/code&gt;. Here we can to see that our service messages have been logged!&lt;/p&gt;

&lt;h2&gt;
  
  
  Variables
&lt;/h2&gt;

&lt;p&gt;Let's refactor our configuration to reduce duplication. We have hard coded the region, which makes it difficult to use this service in a different region.&lt;/p&gt;

&lt;p&gt;Variables in OpenTofu allow us to pass dynamic values to our configuration and always use the following syntax.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;

&lt;span class="k"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"variable_name"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="c1"&gt;# Configuration block&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;variable_name:&lt;/code&gt;&lt;/strong&gt; Is the name for the variable, which must be unique among all variables in the same configuration. This name is used to assign a value to the variable from outside and to reference the variable's value from within the module&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The &lt;strong&gt;&lt;code&gt;Configuration block&lt;/code&gt;&lt;/strong&gt; specifies the type and description of the variable being declared.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's add a dynamic variable for our region.&lt;/p&gt;

&lt;p&gt;Add the following to your &lt;code&gt;main.tf&lt;/code&gt; file:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

variable "region" {
  description = "The aws region to deploy the infrasctructure to."
  type = string
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now refactor the &lt;code&gt;provider&lt;/code&gt; block to use the variable.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;

&lt;span class="k"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;"aws"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;region&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;region&lt;/span&gt; &lt;span class="c1"&gt;# reference the 'region' variable&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Let's deploy our refactored code and pass in a value for the region.&lt;/p&gt;

&lt;p&gt;Run the following command in your terminal.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

tofu apply &lt;span class="nt"&gt;--var&lt;/span&gt; &lt;span class="s2"&gt;"region=us-west-1"&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;You should see the following output.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

No changes. Your infrastructure matches the configuration.


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Notice that nothing changed in our deployment plan! This is what we expect, since we are deploy essentially the same infrastructure configuration, just refactored for maintainability.&lt;/p&gt;

&lt;h2&gt;
  
  
  Destroy The Infrastructure
&lt;/h2&gt;

&lt;p&gt;When you are ready, we can easily remove all the infrastructure resources we created by running &lt;code&gt;tofu destroy&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;You should see the following output.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

Destroy &lt;span class="nb"&gt;complete&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt; Resources: 4 destroyed.


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;You should also see a deployment plan, similar to the ones we saw when running &lt;code&gt;tofu plan&lt;/code&gt; and &lt;code&gt;tofu apply&lt;/code&gt;. &lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this post we reviewed the basics of OpenTofu and how to get started provisioning AWS infrastructure. We reviewed providers, resources, data sources and variables. Hopefully this summary will help those who are completely new OpenTofu and its predecessor Terraform.&lt;/p&gt;

&lt;p&gt;For those who are already familiar with Terraform or have at least heard of it before, you may be wondering why we would even use Open Tofu in the first place.&lt;/p&gt;

&lt;h4&gt;
  
  
  Not So Straight Forward Terraform
&lt;/h4&gt;

&lt;p&gt;While Terraform is an incredible tool, its parent company Hashicorp recently changed Terraform's product license from the Mozilla Public License (v2.0) to a Business Source License (v1.1), effectively close sourcing all new versions of Terraform. In addition, Hashicorp also changed their terms of service for their (provider registry)[&lt;a href="https://github.com/opentofu/roadmap/issues/24#issuecomment-1699535216" rel="noopener noreferrer"&gt;https://github.com/opentofu/roadmap/issues/24#issuecomment-1699535216&lt;/a&gt;], making it a significant legal risk to provision AWS infrastructure with Terraform.&lt;/p&gt;

&lt;p&gt;Overnight, thousands of organizations were required to purchase a Hashicorp license if they wanted to keep using Terraform to manage and provision their infrastructure. &lt;/p&gt;

&lt;p&gt;But then, there was Tofu.&lt;/p&gt;

&lt;p&gt;Within (one month)[&lt;a href="https://opentofu.org/blog/the-opentofu-fork-is-now-available/" rel="noopener noreferrer"&gt;https://opentofu.org/blog/the-opentofu-fork-is-now-available/&lt;/a&gt;] of Hashicorp's license change, the good people behind OpenTofu, successfully forked Terraform v1.5 and made it open source and completely backwards compatible with existing Terraform code bases. In addition, OpenTofu has recently launched an open source provider registry, making it a complete and truly open source replacement for Terraform. Amazing!&lt;/p&gt;

&lt;p&gt;OpenTofu is an incredible achievement for the open source community and demonstrates the beauty and power of open source software. I am excited to support the project and help the project grow. I hope you will join me!&lt;/p&gt;

</description>
      <category>aws</category>
      <category>opensource</category>
      <category>infrastructureascode</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
