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