tech.guitarrapc.cóm

Technical updates

Pulumi Workaround

この記事は、Pulumi dotnet Advent Calendar 2019 の25日目です。

qiita.com

最終日は、Pulumi でこんなときどうするのかをまとめていきます。

目次

TL;DR

Pulumi でトラブルに遭遇したときにどのように回避するかのワークアラウドです。

随時更新予定。

Pulumi.Aws.Iam.Invokes の Input<T>Output<T> の依存解決を行えない

Pulumi の DataSourceは、値が決定的に定まっていることを前提にしているため、 Output<T> を受けることを想定していません。

つまり以下のコードは失敗します。

    using System.Collections.Generic;
    using System.Threading.Tasks;
    using Pulumi;
    using Pulumi.Aws.Iam;
    using Pulumi.Aws.Iam.Inputs;
    
    class Program
    {
        static Task<int> Main()
        {
            return Deployment.RunAsync(async () =>
            {
                var policy = await Pulumi.Aws.Iam.Invokes.GetPolicyDocument(new GetPolicyDocumentArgs
                {
                    Statements = new[] {
                        new GetPolicyDocumentStatementsArgs
                        {
                            Actions = "sts:AssumeRole",
                            Effect = "Allow",
                            Principals = new GetPolicyDocumentStatementsPrincipalsArgs
                            {
                                Type = "Service",
                                Identifiers = "ec2.amazonaws.com",
                            }
                        },
                    },
                });
                var role = new Pulumi.Aws.Iam.Role($"role", new RoleArgs
                {
                    AssumeRolePolicy = policy.Json,
                });
    
                var assumepolicy = await Pulumi.Aws.Iam.Invokes.GetPolicyDocument(new GetPolicyDocumentArgs
                {
                    Statements = new[] {
                        new GetPolicyDocumentStatementsArgs
                        {
                            Actions = "sts:AssumeRole",
                            Effect = "Allow",
                            Principals = new GetPolicyDocumentStatementsPrincipalsArgs
                            {
                                Type = "Service",
                                Identifiers = "ec2.amazonaws.com",
                            }
                        },
                        new GetPolicyDocumentStatementsArgs
                        {
                            Actions = "sts:AssumeRole",
                            Effect = "Allow",
                            Principals = new GetPolicyDocumentStatementsPrincipalsArgs
                            {
                                Type = "AWS",
                                // throws exception here!
                                Identifiers = role.Arn,
                            }
                        }
                    }
                });
    
                return new Dictionary<string, object>
                {
                    { "arn", role.Arn },
                    { "assumepolicy", assumepolicy.Json },
                };
            });
        }
    }

もし Output<T> な値を受けて、Data Source の取得を行いたい場合は、Output<T>.Apply でDataSource を Output の結果を受けて実行されるようにし、出力も Output<T> に変換します。

    using System.Collections.Generic;
    using System.Threading.Tasks;
    using Pulumi;
    using Pulumi.Aws.Iam;
    using Pulumi.Aws.Iam.Inputs;
    
    class Program
    {
        static Task<int> Main()
        {
            return Deployment.RunAsync(async () =>
            {
                var policy = await Pulumi.Aws.Iam.Invokes.GetPolicyDocument(new GetPolicyDocumentArgs
                {
                    Statements = new[] {
                        new GetPolicyDocumentStatementsArgs
                        {
                            Actions = "sts:AssumeRole",
                            Effect = "Allow",
                            Principals = new GetPolicyDocumentStatementsPrincipalsArgs
                            {
                                Type = "Service",
                                Identifiers = "ec2.amazonaws.com",
                            }
                        },
                    },
                });
                var role = new Pulumi.Aws.Iam.Role($"role", new RoleArgs
                {
                    AssumeRolePolicy = policy.Json,
                });
    
                var assumepolicy = role.Arn.Apply(roleArn => Pulumi.Aws.Iam.Invokes.GetPolicyDocument(new GetPolicyDocumentArgs
                {
                    Statements = new[] {
                        new GetPolicyDocumentStatementsArgs
                        {
                            Actions = "sts:AssumeRole",
                            Effect = "Allow",
                            Principals = new GetPolicyDocumentStatementsPrincipalsArgs
                            {
                                Type = "Service",
                                Identifiers = "ec2.amazonaws.com",
                            }
                        },
                        new GetPolicyDocumentStatementsArgs
                        {
                            Actions = "sts:AssumeRole",
                            Effect = "Allow",
                            Principals = new GetPolicyDocumentStatementsPrincipalsArgs
                            {
                                Type = "AWS",
                                Identifiers = roleArn,
                            }
                        }
                    }
                }));
    
                // you can pass assumepolicy to other Resource.
    
                return new Dictionary<string, object>
                {
                    { "arn", role.Arn },
                    { "assumepolicy", assumepolicy.Json },
                };
            });
        }
    }

Pulumi-aws の destroy で vpc 削除が何度やっても失敗する

pulumi destory をしたときに、vpc の削除がどうしても失敗することがあります。

    error: Plan apply failed: deleting urn:pulumi:ekscluster::sandbox::pkg:component:ekscluster$pkg:component:network$aws:ec2/vpc:Vpc::sandbox-network-vpc: Error deleting VPC: DependencyViolation: The vpc 'vpc-052ade5d4667965e8' has dependencies and cannot be deleted.

これは Secutiry Group で、自身の Security Group を参照しているときにおこります。 特に、EKS Cluster などが自動的に作成した Security Groupで、自分自身の Security Group を参照しているときに起こりま す。 Pulumi が知っていれば自分で解消するので、知らない状況を作らないようにするのがいいでしょう。

と思っても、Kubernetes 1.14 + eks.3 以降は、EKS デフォルトの挙動として PulumiなどAPIで指定したセキュリティグループは Addtional Security Groupになり、EKS のデフォルト Security Groupに自身を許可するSGを自分で作るようになった。ので、破綻する...。

pulumi up の preview と実行でそれぞれ dotnet build がかかる

pulumi up を実行すると、Preview と Up が実行されます。

  • preview が実行されプロンプトが表示
  • yes を選択するとpreview の内容が適用されます

yes を選択したときの適用は、一見すると preview の内容ですが、実際はC# コードが再度評価されて、再度 dotnet build が実行されます。

そのため、pulumi up をして yes を選択する前に Visual Studio 上で Pulumi dotnet のコードを変更すると Preview と違う結果が実行されます。

注意しましょう。

pulumi で作成したリソースに直接変更を加えられてもpulumi が検出しない

pulumi は、デフォルトでは実リソースと自分のStateを同期しません。 そのため、該当リソースにコード変更をしない限り気づけません。

terraform のように、コード変更なしに気づくためには、pulumi refreshpulumi up --refresh を行います。

これによって、Pulumi の State と実リソースの状態が同期されて、コードに変更がなくても差分として検出されるようになります。

pulumi のリソースをプロジェクト間で移動させたい

残念ながらサポートされてません。

Support moving resources between projects · Issue #3389 · pulumi/pulumi

他プロジェクトのリソースを参照したい

terraform の remote state 的なものは、pulumi では StackReference といいますが、C# ではサポートされていません。

TypeScript ではサポートされています。

import * as k8s from "@pulumi/kubernetes";
import * as pulumi from "@pulumi/pulumi";
const env = pulumi.getStack();
const infra = new pulumi.StackReference(`acmecorp/infra/${env}`);
const provider = new k8s.Provider("k8s", { kubeconfig: infra.getOutput("kubeConfig") });
const service = new k8s.core.v1.Service(..., { provider: provider });

https://www.pulumi.com/docs/intro/concepts/organizing-stacks-projects/#inter-stack-dependencies

teraform から pulumi に移行する

やる意義があるのならやるのは選択がでるでしょう。

terraform から TypeScript への移行プログラムはあります。

pulumi/tf2pulumi: A tool to convert Terraform projects to Pulumi TypeScript programs

他の言語バインディングは今のところ見つからない。

どのみちImport 祭りになるのはちょっと辛いですが。

Adopting Existing Cloud Resources into Pulumi | Pulumi