diff --git a/fargate-v2.cfhighlander.rb b/fargate-v2.cfhighlander.rb index 77b0e00..90eea26 100644 --- a/fargate-v2.cfhighlander.rb +++ b/fargate-v2.cfhighlander.rb @@ -1,6 +1,6 @@ CfhighlanderTemplate do - DependsOn 'lib-iam@0.1.0' + DependsOn 'lib-iam@0.2.0' DependsOn 'lib-ec2@0.1.0' Parameters do diff --git a/spec/iam_policy_spec.rb b/spec/iam_policy_spec.rb new file mode 100644 index 0000000..3b783f5 --- /dev/null +++ b/spec/iam_policy_spec.rb @@ -0,0 +1,172 @@ +require 'yaml' + +describe 'compiled component fargate-v2' do + + context 'cftest' do + it 'compiles test' do + expect(system("cfhighlander cftest #{@validate} --tests tests/iam_policy.test.yaml")).to be_truthy + end + end + + let(:template) { YAML.load_file("#{File.dirname(__FILE__)}/../out/tests/iam_policy/fargate-v2.compiled.yaml") } + + context "Resource" do + + + context "SecurityGroup" do + let(:resource) { template["Resources"]["SecurityGroup"] } + + it "is of type AWS::EC2::SecurityGroup" do + expect(resource["Type"]).to eq("AWS::EC2::SecurityGroup") + end + + it "to have property VpcId" do + expect(resource["Properties"]["VpcId"]).to eq({"Ref"=>"VPCId"}) + end + + it "to have property GroupDescription" do + expect(resource["Properties"]["GroupDescription"]).to eq("fargate-v2 fargate service") + end + + end + + context "EcsFargateService" do + let(:resource) { template["Resources"]["EcsFargateService"] } + + it "is of type AWS::ECS::Service" do + expect(resource["Type"]).to eq("AWS::ECS::Service") + end + + it "to have property Cluster" do + expect(resource["Properties"]["Cluster"]).to eq({"Ref"=>"EcsCluster"}) + end + + it "to have property DesiredCount" do + expect(resource["Properties"]["DesiredCount"]).to eq({"Ref"=>"DesiredCount"}) + end + + it "to have property DeploymentConfiguration" do + expect(resource["Properties"]["DeploymentConfiguration"]).to eq({"MinimumHealthyPercent"=>{"Ref"=>"MinimumHealthyPercent"}, "MaximumPercent"=>{"Ref"=>"MaximumPercent"}}) + end + + it "to have property EnableExecuteCommand" do + expect(resource["Properties"]["EnableExecuteCommand"]).to eq(false) + end + + it "to have property TaskDefinition" do + expect(resource["Properties"]["TaskDefinition"]).to eq({"Ref"=>"Task"}) + end + + it "to have property LaunchType" do + expect(resource["Properties"]["LaunchType"]).to eq("FARGATE") + end + + it "to have property NetworkConfiguration" do + expect(resource["Properties"]["NetworkConfiguration"]).to eq({"AwsvpcConfiguration"=>{"AssignPublicIp"=>"DISABLED", "SecurityGroups"=>[{"Ref"=>"SecurityGroup"}], "Subnets"=>{"Ref"=>"SubnetIds"}}}) + end + + end + + context "LogGroup" do + let(:resource) { template["Resources"]["LogGroup"] } + + it "is of type AWS::Logs::LogGroup" do + expect(resource["Type"]).to eq("AWS::Logs::LogGroup") + end + + it "to have property LogGroupName" do + expect(resource["Properties"]["LogGroupName"]).to eq({"Ref"=>"AWS::StackName"}) + end + + it "to have property RetentionInDays" do + expect(resource["Properties"]["RetentionInDays"]).to eq("7") + end + + end + + context "TaskRole" do + let(:resource) { template["Resources"]["TaskRole"] } + + it "is of type AWS::IAM::Role" do + expect(resource["Type"]).to eq("AWS::IAM::Role") + end + + it "to have property AssumeRolePolicyDocument" do + expect(resource["Properties"]["AssumeRolePolicyDocument"]).to eq({"Version"=>"2012-10-17", "Statement"=>[{"Effect"=>"Allow", "Principal"=>{"Service"=>"ecs-tasks.amazonaws.com"}, "Action"=>"sts:AssumeRole"}, {"Effect"=>"Allow", "Principal"=>{"Service"=>"ssm.amazonaws.com"}, "Action"=>"sts:AssumeRole"}]}) + end + + it "to have property Path" do + expect(resource["Properties"]["Path"]).to eq("/") + end + + it "to have property Policies" do + expect(resource["Properties"]["Policies"]).to eq([{"PolicyName"=>"fargate_default_policy", "PolicyDocument"=>{"Statement"=>[{"Sid"=>"fargatedefaultpolicy", "Action"=>["logs:GetLogEvents"], "Resource"=>[{"Fn::GetAtt"=>["LogGroup", "Arn"]}], "Effect"=>"Allow"}]}}, {"PolicyName"=>"create-spot-service-liked-role", "PolicyDocument"=>{"Statement"=>[{"Sid"=>"createspotservicelikedrole", "Action"=>["iam:CreateServiceLinkedRole"], "Resource"=>["*"], "Effect"=>"Allow", "Condition"=>{"StringLike"=>{"iam:AWSServiceName"=>"spot.amazonaws.com"}}}]}}, {"PolicyName"=>"cross-account-sts", "PolicyDocument"=>{"Statement"=>[{"Sid"=>"crossaccountsts", "Action"=>["sts:AssumeRole"], "Resource"=>["*"], "Effect"=>"Allow"}]}}, {"PolicyName"=>"get-identity", "PolicyDocument"=>{"Statement"=>[{"Sid"=>"getidentity", "Action"=>["sts:GetCallerIdentity"], "Resource"=>["*"], "Effect"=>"Allow"}]}}, {"PolicyName"=>"iam-pass-role", "PolicyDocument"=>{"Statement"=>[{"Sid"=>"iampassrole", "Action"=>["iam:ListRoles", "iam:PassRole", "iam:ListInstanceProfiles"], "Resource"=>["*"], "Effect"=>"Allow"}]}}, {"PolicyName"=>"ec2-fleet-plugin", "PolicyDocument"=>{"Statement"=>[{"Sid"=>"ec2fleetplugin", "Action"=>["ec2:*"], "Resource"=>["*"], "Effect"=>"Allow"}]}}, {"PolicyName"=>"s3-list-ciinabox-bucket", "PolicyDocument"=>{"Statement"=>[{"Sid"=>"s3listciinaboxbucket", "Action"=>["s3:ListBucket", "s3:GetBucketLocation"], "Resource"=>[{"Fn::Sub"=>"arn:aws:s3:::bucket"}], "Effect"=>"Allow"}]}}, {"PolicyName"=>"s3-rw", "PolicyDocument"=>{"Statement"=>[{"Sid"=>"s3rw", "Action"=>["s3:GetObject", "s3:GetObjectAcl", "s3:GetObjectVersion", "s3:PutObject", "s3:PutObjectAcl"], "Resource"=>[{"Fn::Sub"=>"arn:aws:s3:::bucket/*"}], "Effect"=>"Allow"}]}}, {"PolicyName"=>"secretsmanager-list", "PolicyDocument"=>{"Statement"=>[{"Sid"=>"secretsmanagerlist", "Action"=>["secretsmanager:ListSecrets"], "Resource"=>["*"], "Effect"=>"Allow"}]}}, {"PolicyName"=>"secretsmanager-get", "PolicyDocument"=>{"Statement"=>[{"Sid"=>"secretsmanagerget", "Action"=>["secretsmanager:GetSecretValue"], "Resource"=>[{"Fn::Sub"=>"arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:/${EnvironmentName}/jenkins/*"}], "Effect"=>"Allow"}]}}, {"PolicyName"=>"ssm-parameters", "PolicyDocument"=>{"Statement"=>[{"Sid"=>"ssmparameters", "Action"=>["ssm:GetParameter", "ssm:GetParametersByPath"], "Resource"=>[{"Fn::Sub"=>"arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/ciinabox/*"}, {"Fn::Sub"=>"arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/aws/*"}], "Effect"=>"Allow"}]}}, {"PolicyName"=>"sns-publish", "PolicyDocument"=>{"Statement"=>[{"Sid"=>"snspublish", "Action"=>["sns:Publish"], "Resource"=>["*"], "Effect"=>"Allow"}]}}, {"PolicyName"=>"ecr-manange-repos", "PolicyDocument"=>{"Statement"=>[{"Sid"=>"ecrmanangerepos", "Action"=>["ecr:*"], "Resource"=>["*"], "Effect"=>"Allow"}]}}, {"PolicyName"=>"codeartifact-manange-repos", "PolicyDocument"=>{"Statement"=>[{"Sid"=>"codeartifactmanangerepos", "Action"=>["codeartifact:*"], "Resource"=>["*"], "Effect"=>"Allow"}]}}, {"PolicyName"=>"codecommit-pull", "PolicyDocument"=>{"Statement"=>[{"Sid"=>"codecommitpull", "Action"=>["codecommit:BatchGet*", "codecommit:BatchDescribe*", "codecommit:Describe*", "codecommit:EvaluatePullRequestApprovalRules", "codecommit:Get*", "codecommit:List*", "codecommit:GitPull"], "Resource"=>["*"], "Effect"=>"Allow"}]}}, {"PolicyName"=>"ecs-manage-tasks", "PolicyDocument"=>{"Statement"=>[{"Sid"=>"ecsmanagetasks0", "Action"=>["ecs:RunTask", "ecs:DescribeTasks", "ecs:RegisterTaskDefinition", "ecs:ListClusters", "ecs:DescribeContainerInstances", "ecs:ListTaskDefinitions", "ecs:DescribeTaskDefinition", "ecs:DeregisterTaskDefinition"], "Resource"=>["*"], "Effect"=>"Allow"}, {"Sid"=>"ecsmanagetasks1", "Action"=>["ecs:ListContainerInstances", "ecs:DescribeClusters"], "Resource"=>[{"Fn::Sub"=>"arn:aws:ecs:${AWS::Region}:${AWS::AccountId}:cluster/my-cluster"}], "Effect"=>"Allow"}, {"Sid"=>"ecsmanagetasks2", "Action"=>["ecs:RunTask"], "Resource"=>[{"Fn::Sub"=>"arn:aws:ecs:${AWS::Region}:${AWS::AccountId}:task-definition/*"}], "Effect"=>"Allow", "Condition"=>{"ArnEquals"=>{"ecs:cluster"=>[{"Fn::Sub"=>"arn:aws:ecs:${AWS::Region}:${AWS::AccountId}:cluster/my-cluster"}]}}}, {"Sid"=>"ecsmanagetasks3", "Action"=>["ecs:StopTask"], "Resource"=>["arn:aws:ecs:*:*:task/*"], "Effect"=>"Allow", "Condition"=>{"ArnEquals"=>{"ecs:cluster"=>[{"Fn::Sub"=>"arn:aws:ecs:${AWS::Region}:${AWS::AccountId}:cluster/my-cluster"}]}}}, {"Sid"=>"ecsmanagetasks4", "Action"=>["ecs:DescribeTasks"], "Resource"=>["arn:aws:ecs:*:*:task/*"], "Effect"=>"Allow", "Condition"=>{"ArnEquals"=>{"ecs:cluster"=>[{"Fn::Sub"=>"arn:aws:ecs:${AWS::Region}:${AWS::AccountId}:cluster/my-cluster"}]}}}]}}]) + end + + end + + context "ExecutionRole" do + let(:resource) { template["Resources"]["ExecutionRole"] } + + it "is of type AWS::IAM::Role" do + expect(resource["Type"]).to eq("AWS::IAM::Role") + end + + it "to have property AssumeRolePolicyDocument" do + expect(resource["Properties"]["AssumeRolePolicyDocument"]).to eq({"Version"=>"2012-10-17", "Statement"=>[{"Effect"=>"Allow", "Principal"=>{"Service"=>"ecs-tasks.amazonaws.com"}, "Action"=>"sts:AssumeRole"}, {"Effect"=>"Allow", "Principal"=>{"Service"=>"ssm.amazonaws.com"}, "Action"=>"sts:AssumeRole"}]}) + end + + it "to have property Path" do + expect(resource["Properties"]["Path"]).to eq("/") + end + + it "to have property ManagedPolicyArns" do + expect(resource["Properties"]["ManagedPolicyArns"]).to eq(["arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"]) + end + + end + + context "Task" do + let(:resource) { template["Resources"]["Task"] } + + it "is of type AWS::ECS::TaskDefinition" do + expect(resource["Type"]).to eq("AWS::ECS::TaskDefinition") + end + + it "to have property ContainerDefinitions" do + expect(resource["Properties"]["ContainerDefinitions"]).to eq([{"Name"=>"proxy", "Image"=>{"Fn::Join"=>["", ["", "nginx", ":", "latest"]]}, "LogConfiguration"=>{"LogDriver"=>"awslogs", "Options"=>{"awslogs-group"=>{"Ref"=>"LogGroup"}, "awslogs-region"=>{"Ref"=>"AWS::Region"}, "awslogs-stream-prefix"=>"proxy"}}, "PortMappings"=>[{"ContainerPort"=>80}]}]) + end + + it "to have property RequiresCompatibilities" do + expect(resource["Properties"]["RequiresCompatibilities"]).to eq(["FARGATE"]) + end + + it "to have property Cpu" do + expect(resource["Properties"]["Cpu"]).to eq(256) + end + + it "to have property Memory" do + expect(resource["Properties"]["Memory"]).to eq(512) + end + + it "to have property NetworkMode" do + expect(resource["Properties"]["NetworkMode"]).to eq("awsvpc") + end + + it "to have property TaskRoleArn" do + expect(resource["Properties"]["TaskRoleArn"]).to eq({"Ref"=>"TaskRole"}) + end + + it "to have property ExecutionRoleArn" do + expect(resource["Properties"]["ExecutionRoleArn"]).to eq({"Ref"=>"ExecutionRole"}) + end + + it "to have property Tags" do + expect(resource["Properties"]["Tags"]).to eq([{"Key"=>"Name", "Value"=>"fargatev2Task"}, {"Key"=>"Environment", "Value"=>{"Ref"=>"EnvironmentName"}}, {"Key"=>"EnvironmentType", "Value"=>{"Ref"=>"EnvironmentType"}}]) + end + + end + + end + +end \ No newline at end of file diff --git a/tests/iam_policy.test.yaml b/tests/iam_policy.test.yaml new file mode 100644 index 0000000..3d65adc --- /dev/null +++ b/tests/iam_policy.test.yaml @@ -0,0 +1,117 @@ +test_metadata: + type: config + name: iam_policy + description: test setting iam policies + +task_definition: + proxy: + image: nginx + ports: + - 80 + +iam_policies: + create-spot-service-liked-role: + action: + - iam:CreateServiceLinkedRole + condition: + StringLike: + iam:AWSServiceName: spot.amazonaws.com + cross-account-sts: + action: + - sts:AssumeRole + get-identity: + action: + - sts:GetCallerIdentity + iam-pass-role: + action: + - iam:ListRoles + - iam:PassRole + - iam:ListInstanceProfiles + ec2-fleet-plugin: + action: + - ec2:* + s3-list-ciinabox-bucket: + action: + - s3:ListBucket + - s3:GetBucketLocation + resource: + - Fn::Sub: arn:aws:s3:::bucket + s3-rw: + action: + - s3:GetObject + - s3:GetObjectAcl + - s3:GetObjectVersion + - s3:PutObject + - s3:PutObjectAcl + resource: + - Fn::Sub: arn:aws:s3:::bucket/* + secretsmanager-list: + action: + - secretsmanager:ListSecrets + secretsmanager-get: + action: + - secretsmanager:GetSecretValue + resource: + - Fn::Sub: arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:/${EnvironmentName}/jenkins/* + ssm-parameters: + action: + - ssm:GetParameter + - ssm:GetParametersByPath + resource: + - Fn::Sub: arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/ciinabox/* + - Fn::Sub: arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/aws/* + sns-publish: + action: + - sns:Publish + ecr-manange-repos: + action: + - ecr:* + codeartifact-manange-repos: + action: + - codeartifact:* + codecommit-pull: + action: + - codecommit:BatchGet* + - codecommit:BatchDescribe* + - codecommit:Describe* + - codecommit:EvaluatePullRequestApprovalRules + - codecommit:Get* + - codecommit:List* + - codecommit:GitPull + ecs-manage-tasks: + - action: + - ecs:RunTask + - ecs:DescribeTasks + - ecs:RegisterTaskDefinition + - ecs:ListClusters + - ecs:DescribeContainerInstances + - ecs:ListTaskDefinitions + - ecs:DescribeTaskDefinition + - ecs:DeregisterTaskDefinition + - action: + - "ecs:ListContainerInstances" + - "ecs:DescribeClusters" + resource: + - Fn::Sub: "arn:aws:ecs:${AWS::Region}:${AWS::AccountId}:cluster/my-cluster" + - action: + - "ecs:RunTask" + condition: + ArnEquals: + ecs:cluster: + - Fn::Sub: "arn:aws:ecs:${AWS::Region}:${AWS::AccountId}:cluster/my-cluster" + resource: + - Fn::Sub: "arn:aws:ecs:${AWS::Region}:${AWS::AccountId}:task-definition/*" + - action: + - "ecs:StopTask" + condition: + ArnEquals: + ecs:cluster: + - Fn::Sub: "arn:aws:ecs:${AWS::Region}:${AWS::AccountId}:cluster/my-cluster" + resource: "arn:aws:ecs:*:*:task/*" + - action: + - "ecs:DescribeTasks" + condition: + ArnEquals: + ecs:cluster: + - Fn::Sub: "arn:aws:ecs:${AWS::Region}:${AWS::AccountId}:cluster/my-cluster" + resource: "arn:aws:ecs:*:*:task/*"