本文へスキップ

カスタム条件

設定内のいくつかの種類のオブジェクトに対して、カスタムエラーメッセージを生成する条件を作成できます。たとえば、入力変数に条件を追加して、受信したイメージIDの形式が適切かどうかを確認できます。カスタム条件は仮定を捉えることで、将来の保守者が設定の設計と意図を理解するのに役立ちます。また、早期かつコンテキスト内で有用なエラー情報を返すため、コンシューマが設定の問題をより簡単に診断するのに役立ちます。

このページでは、以下について説明します。

ユースケースに合わせたカスタム条件の選択

OpenTofuのさまざまなカスタム条件は、さまざまな状況に最適です。次の広範なガイドラインを使用して、ユースケースに最適なカスタム条件を選択してください。

  1. アサーションを使用したチェックブロックは、インフラストラクチャ全体を検証します。さらに、チェックブロックはOpenTofu操作の全体的な実行を妨げたり、ブロックしたりしません。
  2. 検証条件または出力事後条件を使用すると、設定の入力と出力が特定の要件を満たしていることを確認できます。
  3. リソースの事前条件と事後条件を使用すると、OpenTofuが予測可能な結果で設定を生成することを検証できます。

特定のカスタム条件を使用するタイミングの詳細については、事前条件と事後条件の選択チェックまたはその他のカスタム条件の選択を参照してください。

入力変数の検証

カスタム条件を指定するには、variableブロック内に1つ以上のvalidationブロックを追加します。各検証にはcondition引数が必要です。これは、値が有効な場合はtrueを、無効な場合はfalseを返す必要がある式です。この式は、含まれている変数のみを参照でき、エラーを生成できません。

条件がfalseと評価された場合、OpenTofuはerror_message式の結果を含むエラーメッセージを生成します。複数の検証を宣言した場合、OpenTofuは失敗したすべての条件のエラーメッセージを返します。

次の例では、AMI IDに有効な構文があるかどうかを確認します。

コードブロック
variable "image_id" {
type = string
description = "The id of the machine image (AMI) to use for the server."

validation {
condition = length(var.image_id) > 4 && substr(var.image_id, 0, 4) == "ami-"
error_message = "The image_id value must be a valid AMI id, starting with \"ami-\"."
}
}

式の失敗によって検証の決定がなされる場合は、次の例に示すようにcan関数を使用します。

コードブロック
variable "image_id" {
type = string
description = "The id of the machine image (AMI) to use for the server."

validation {
# regex(...) fails if it cannot find a match
condition = can(regex("^ami-", var.image_id))
error_message = "The image_id value must be a valid AMI id, starting with \"ami-\"."
}
}

事前条件と事後条件

preconditionブロックとpostconditionブロックを使用して、リソース、データソース、出力のカスタムルールを作成します。

OpenTofuは、関連付けられているオブジェクトを評価するに事前条件をチェックし、オブジェクトを評価したに事後条件をチェックします。OpenTofuは、できるだけ早くカスタム条件を評価しますが、未知の値に依存する条件は、適用フェーズまで延期する必要があります。詳細については、適用時のみチェックされる条件を参照してください。

使用方法

各事前条件と事後条件にはcondition引数が必要です。これは、条件が満たされた場合はtrueを、無効な場合はfalseを返す必要がある式です。この式は、同じモジュール内の他のオブジェクトを参照できますが、参照によって循環依存関係が作成されないようにする必要があります。リソースの事後条件では、selfオブジェクトを使用して、それらが設定されているリソースの各インスタンスの属性を参照することもできます。

条件がfalseと評価された場合、OpenTofuはerror_message式の結果を含むエラーメッセージを生成します。複数の事前条件または事後条件を宣言した場合、OpenTofuは失敗したすべての条件のエラーメッセージを返します。

以下の例では、事後条件を使用して、呼び出し元が誤ったシステムコンポーネントを対象としたAMIを誤って提供していないかどうかを検出します。

コードブロック
data "aws_ami" "example" {
id = var.aws_ami_id

lifecycle {
# The AMI ID must refer to an existing AMI that has the tag "nomad-server".
postcondition {
condition = self.tags["Component"] == "nomad-server"
error_message = "tags[\"Component\"] must be \"nomad-server\"."
}
}
}

リソースとデータソース

resourceブロックまたはdataブロック内のlifecycleブロックには、preconditionブロックとpostconditionブロックの両方を指定できます。

  • OpenTofuは、既存のcount引数とfor_each引数を評価した後にpreconditionブロックを評価します。これにより、OpenTofuは各インスタンスに対して個別に事前条件を評価し、each.keycount.indexなどをこれらの条件で使用できるようにします。OpenTofuは、リソースの構成引数を評価する前にも事前条件を評価します。事前条件は、引数の評価エラーよりも優先される場合があります。
  • OpenTofuは、管理対象リソースへの変更の計画と適用後、またはデータソースからの読み込み後に、postconditionブロックを評価します。事後条件の失敗は、失敗したリソースに依存する他のリソースへの変更を妨げます。

ほとんどの場合、同じオブジェクトを表すdataブロックとresourceブロックの両方を同じ構成に含めることはお勧めしません。そうすると、OpenTofuがdataブロックの結果がresourceブロックの変更の影響を受ける可能性があることを理解できなくなる可能性があります。ただし、リソース自体が直接エクスポートしないresourceブロックの結果を確認する必要がある場合は、dataブロックをdataブロックの直接的なpostconditionとして配置する限り、そのオブジェクトを安全に確認するためにdataブロックを使用できます。これにより、OpenTofuはdataブロックが他の場所で定義されたオブジェクトのチェックとして機能していることを理解し、正しい順序でアクションを実行できます。

出力

outputブロックには、preconditionブロックを含めることができます。

事前条件は、入力変数のvalidationブロックと対称的な目的を果たすことができます。入力変数の検証では、モジュールが入力について行う仮定をチェックするのに対し、事前条件では、モジュールが出力について行う保証をチェックします。事前条件を使用して、OpenTofuが状態に無効な新しい出力値を保存するのを防ぐことができます。また、該当する場合は、以前の適用からの有効な出力値を保持するためにも使用できます。

OpenTofuは、結果を確定するためにvalue式を評価する前に、出力値の事前条件を評価します。事前条件は、value式における潜在的なエラーよりも優先される場合があります。

次の例は、事前条件と事後条件の使用例を示しています。事前条件と事後条件は、以下の仮定と保証を宣言しています。

  • **AMI IDは、x86_64アーキテクチャのオペレーティングシステムを含むAMIを参照する必要があります。**事前条件は、呼び出し元が誤って異なるアーキテクチャ用のAMIを作成した場合(この仮想マシンがホストすることを目的としたソフトウェアを実行できない可能性があります)を検出します。

  • **EC2インスタンスには、パブリックDNSホスト名が割り当てられている必要があります。**Amazon Web Servicesでは、EC2インスタンスには、特定の方法で構成された仮想ネットワークに属している場合にのみ、パブリックDNSホスト名が割り当てられます。事後条件は、選択した仮想ネットワークが正しく構成されていない場合に検出し、ユーザーにネットワーク設定のデバッグを促します。

  • **EC2インスタンスには、暗号化されたルートボリュームがあります。**事前条件は、ルートボリュームが暗号化されていることを保証します。これは、このEC2インスタンスで実行されているソフトウェアは、暗号化されていないボリュームでもおそらく期待通りに動作するとしてもです。これにより、OpenTofuは、他のコンポーネントが新しいEC2インスタンスに依存する前に、すぐにエラーを生成できます。

コードブロック

data "aws_ami" "example" {
owners = ["amazon"]

filter {
name = "image-id"
values = ["ami-abc123"]
}
}

resource "aws_instance" "example" {
instance_type = "t3.micro"
ami = data.aws_ami.example.id

lifecycle {
# The AMI ID must refer to an AMI that contains an operating system
# for the `x86_64` architecture.
precondition {
condition = data.aws_ami.example.architecture == "x86_64"
error_message = "The selected AMI must be for the x86_64 architecture."
}

# The EC2 instance must be allocated a public DNS hostname.
postcondition {
condition = self.public_dns != ""
error_message = "EC2 instance must be in a VPC that has public DNS hostnames enabled."
}
}
}

data "aws_ebs_volume" "example" {
# Use data resources that refer to other resources to
# load extra data that isn't directly exported by a resource.
#
# Read the details about the root storage volume for the EC2 instance
# declared by aws_instance.example, using the exported ID.

filter {
name = "volume-id"
values = [aws_instance.example.root_block_device.volume_id]
}

# Whenever a data resource is verifying the result of a managed resource
# declared in the same configuration, you MUST write the checks as
# postconditions of the data resource. This ensures OpenTofu will wait
# to read the data resource until after any changes to the managed resource
# have completed.
lifecycle {
# The EC2 instance will have an encrypted root volume.
postcondition {
condition = self.encrypted
error_message = "The server's root volume is not encrypted."
}
}
}

output "api_base_url" {
value = "https://${aws_instance.example.private_dns}:8433/"
}

事前条件と事後条件の選択

検証チェックは、データを生成するリソースの事後条件として、またはデータを使用してリソースまたは出力値の事前条件として実装できることがよくあります。どちらが最も適切かを決定するには、チェックが仮定と保証のどちらを表しているかを検討してください。

仮定には事前条件を使用する

仮定とは、特定のリソースの構成を使用可能にするために真である必要がある条件です。たとえば、aws_instance構成には、指定されたAMIが常にx86_64 CPUアーキテクチャ用に構成されているという仮定を設定できます。

将来の保守担当者が、その条件に依存する他の式に近い場所でそれらを見つけることができるように、仮定には事前条件を使用することをお勧めします。これにより、そのリソースが許可することを意図している内容について、より深く理解できます。

保証には事後条件を使用する

保証とは、構成の残りの部分が依存できるオブジェクトの特性または動作です。たとえば、aws_instance構成には、EC2インスタンスがプライベートDNSレコードを割り当てるネットワークで実行されるという保証を設定できます。

将来の保守担当者が、それらの保証を実装する責任があるリソース構成に近い場所でそれらを見つけることができるように、保証には事後条件を使用することをお勧めします。これにより、構成を変更する際に、どの動作を保持する必要があるかをより簡単に判断できます。

追加の決定要因

事前条件と事後条件を作成する際には、次の点も考慮する必要があります。

  • エラーメッセージで報告するのに最も役立つリソースまたは出力値はどれですか?OpenTofuは、常に条件が宣言された場所でエラーを報告します。
  • どちらのアプローチがより便利ですか?特定のリソースに、すべてそのリソースに関する仮定を行う多くの依存関係がある場合、それらを依存関係ごとに複数回事前条件として宣言するのではなく、リソースの事後条件として一度宣言することが実際的です。
  • 同じ条件または類似の条件を事前条件と事後条件の両方として宣言することは役に立ちますか?これは、事後条件が事前条件とは異なるモジュールにある場合に役立ちます。これは、モジュールが独立して進化する際に互いに検証できるようになるためです。

アサーションを使用したチェック

チェックブロックは、通常の資源ライフサイクルの外側でインフラストラクチャを検証できます。計画と適用ステージの最後に実行され、インフラストラクチャ内の問題を通知するための警告を生成するassertブロックを使用して、カスタム条件を追加できます。

カスタム条件を検証するには、checkブロック内に1つ以上のassertブロックを追加できます。各アサーションには、condition引数が必要です。これは、意図した仮定または保証が満たされている場合はtrue、満たされていない場合はfalseを返すブール式です。condition式は、周囲のcheckブロックで使用可能なリソース、データソース、または変数を参照できます。

次の例では、アサーションを含むチェックブロックを使用して、OpenTofu Webサイトが正常に動作していることを検証しています。

コードブロック
check "health_check" {
data "http" "opentofu_org" {
url = "https://www.opentofu.org"
}

assert {
condition = data.http.opentofu_org.status_code == 200
error_message = "${data.http.opentofu_org.url} returned an unhealthy status code"
}
}

条件がfalseと評価された場合、OpenTofuはerror_message式の結果を含むエラーメッセージを生成します。複数のアサーションを宣言した場合、OpenTofuは失敗したすべての条件に対してエラーメッセージを返します。

クラウドバックエンドでの継続的な検証

クラウドバックエンドは、OpenTofuがインフラストラクチャをプロビジョニングした後も、ワークスペースの構成内のチェックが引き続きパスすることを自動的にチェックできます。たとえば、APIゲートウェイ証明書の有効性を継続的に監視するcheckを作成できます。継続的な検証により、条件が失敗したときに警告が表示されるため、証明書を更新して、次回インフラストラクチャを更新しようとしたときにエラーを回避できます。

条件式

チェックアサーション、入力変数の検証、事前条件、事後条件にはすべて、condition引数が必要です。これは、意図した仮定または保証が満たされている場合はtrue、満たされていない場合はfalseを返すブール式です。

式が有効でブール値の結果を返す限り、条件ではOpenTofuの組み込み関数または言語演算子のいずれかを使用できます。条件式を作成する際には、次の言語機能が特に役立ちます。

論理演算子

複数の条件を組み合わせるには、論理演算子&&(AND)、||(OR)、!(NOT)を使用します。

コードブロック
  condition = var.name != "" && lower(var.name) == var.name

算術演算子(例:a + b)、等価演算子(例:a == b)、比較演算子(例:a < b)も使用できます。詳細については、算術演算子と論理演算子を参照してください。

contains関数

contains関数を使用して、指定された値が事前に定義された有効値のセットのいずれかであるかどうかをテストします。

コードブロック
  condition = contains(["STAGE", "PROD"], var.environment)

length関数

length関数を使用して、コレクションの長さをテストし、空ではないリストまたはマップを要求します。

コードブロック
  condition = length(var.items) != 0

これは、==または!=を使用して別のコレクションと直接比較するよりも優れたアプローチです。これは、比較演算子は、両方のオペランドがまったく同じ型である場合にのみtrueを返すことができ、空のコレクションではしばしば曖昧であるためです。

for

forと関数alltrueおよびanytrueを組み合わせて、条件がコレクションのすべての要素または任意の要素に当てはまるかどうかをテストします。

コードブロック
  condition = alltrue([
for v in var.instances : contains(["t2.micro", "m3.medium"], v.type)
])

can関数

can関数を使用して、式の有効性を条件として簡潔に使用します。指定された式が正常に評価された場合はtrue、エラーを返した場合はfalseを返すため、通常はエラーを返す他のさまざまな関数を条件式の一部として使用できます。

たとえば、canregexと組み合わせて、文字列が特定のパターンに一致するかどうかをテストできます。これは、regexが一致しない文字列が与えられた場合にエラーを返すためです。

コードブロック
  condition = can(regex("^[a-z]+$", var.name))

型変換関数を使用して、値を型または型制約に変換できるかどうかをテストするためにもcanを使用できます。

コードブロック
  # This remote output value must have a value that can
# be used as a string, which includes strings themselves
# but also allows numbers and boolean values.
condition = can(tostring(data.terraform_remote_state.example.outputs["name"]))
コードブロック
  # This remote output value must be convertible to a list
# type of with element type.
condition = can(tolist(data.terraform_remote_state.example.outputs["items"]))

属性アクセスまたはインデックス演算子を使用して、コレクションまたは構造値に特定の要素またはインデックスがあるかどうかをテストするためにもcanを使用できます。

コードブロック
  # var.example must have an attribute named "foo"
condition = can(var.example.foo)
コードブロック
  # var.example must be a sequence with at least one element
condition = can(var.example[0])
# (although it would typically be clearer to write this as a
# test like length(var.example) > 0 to better represent the
# intent of the condition.)

selfオブジェクト

事後条件ブロックでは、selfオブジェクトを使用して、評価対象のインスタンスの属性を参照します。

コードブロック
resource "aws_instance" "example" {
instance_type = "t2.micro"
ami = "ami-abc123"

lifecycle {
postcondition {
condition = self.instance_state == "running"
error_message = "EC2 instance must be running."
}
}
}

eachオブジェクトとcountオブジェクト

for_eachまたはcountが設定されているブロックでは、eachオブジェクトとcountオブジェクトを使用して、チェーンで展開される他のリソースを参照します。

コードブロック
variable "vpc_cidrs" {
type = set(string)
}

data "aws_vpc" "example" {
for_each = var.vpc_cidrs

filter {
name = "cidr"
values = [each.key]
}
}

resource "aws_internet_gateway" "example" {
for_each = data.aws_vpc.example
vpc_id = each.value.id

lifecycle {
precondition {
condition = data.aws_vpc.example[each.key].state == "available"
error_message = "VPC ${each.key} must be available."
}
}
}

エラーメッセージ

入力変数の検証、前提条件、および事後条件にはすべて、error_message引数を指定する必要があります。これには、OpenTofuが条件を満たしていないことを検出した場合に、エラーメッセージの一部として含めるテキストが含まれます。

コードブロック
Error: Resource postcondition failed

with data.aws_ami.example,
on ec2.tf line 19, in data "aws_ami" "example":
72: condition = self.tags["Component"] == "nomad-server"
|----------------
| self.tags["Component"] is "consul-server"

The selected AMI must be tagged with the Component value "nomad-server".

error_message引数には、文字列として評価される任意の式を指定できます。これには、リテラル文字列、heredoc、およびテンプレート式が含まれます。nulllist、またはmap型のアイテムをフォーマットされた文字列に変換するには、format関数を使用できます。複数行のエラーメッセージがサポートされており、先頭に空白のある行は折り返されません。

エラーメッセージは、OpenTofu自身のエラーメッセージと同様のスタイルで、1つ以上の完全な文で記述することをお勧めします。OpenTofuは、問題を検出したリソースの名前と、条件式に含まれる外部値と共にメッセージを表示します。

適用時のみチェックされる条件

OpenTofuは、カスタム条件をできるだけ早く評価します。

入力変数の検証は変数値のみを参照できるため、OpenTofuは常にそれらをただちに評価します。チェックアサーション、前提条件、および事後条件は、条件に関連付けられた値が構成の適用前または適用後に既知であるかどうかをOpenTofuが評価するかに依存します。

  • 適用前に既知: OpenTofuは、計画フェーズ中に条件をチェックします。たとえば、別のリソースから生成されていない限り、OpenTofuは計画中にイメージIDの値を知ることができます。
  • 適用後に既知: OpenTofuは、適用フェーズまで条件のチェックを遅延させます。たとえば、AWSはEC2インスタンスの起動時にルートボリュームIDのみを割り当てるため、OpenTofuは適用時までこの値を知ることができません。

適用フェーズ中に、失敗した前提条件は、関連するリソースに対する計画されたアクションの実装をOpenTofuが実行するのを妨げます。ただし、失敗した事後条件は、OpenTofuがこれらのアクションを既に実装した後に処理を停止します。失敗した事後条件は、リソースに依存するそれ以降の下流のアクションを防止しますが、OpenTofuが既に実行したアクションを元に戻すことはありません。

OpenTofuは、完全な構成の最初の作成時よりも、後続の変更を適用する際に通常、情報が少ないです。したがって、OpenTofuは、最初の作成時には適用時に条件をチェックし、後続の更新時には計画時に条件をチェックする場合があります。