Need to apply IAMPass Role to specific environment - json

I have cloudformation template. Here we have multiple environments(dev,qa,uat) and need to use same template for all environments.
In template Under "Action": ["iam:PassRole"] there are 4 resources, 3 resources are belongs to qa. When am deploying code on dev and uat Env, qa resources are applying to dev and uat environment as well but I need to create these 3 qa resources only for qa environment. I tried some conditions but isn't working. Is there any approach for this.
Please find below template code.
{
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {
"CloudFormationRole": {
"Type": "AWS::IAM::Role",
"Description": "Service role in IAM for AWS CloudFormation",
"Properties": {
"RoleName": {
"Fn::Sub": "${Environment}-workflow-CloudFormationRole"
},
"Path": "/",
"Policies": [
{
"PolicyName": "WorkerCloudFormationRolePolicy",
"PolicyDocument": {
"Statement": [
{
"Action": [
"lambda:AddPermission",
"lambda:PutFunctionEventInvokeConfig",
"lambda:UpdateFunctionEventInvokeConfig"
],
"Resource": {
"Fn::Sub": "arn:aws:lambda:function:orderser-${Environment}-workflow-*"
},
"Effect": "Allow"
},
{
"Action": [
"iam:PassRole"
],
"Resource": [
{"Fn::Sub": "arn:aws:iam::role/orderser-workflow-*"},
{"Fn::Sub": "arn:aws:iam::role/orderserv-qa-workflowLambdaRole1"},
{"Fn::Sub": "arn:aws:iam::role/orderserv-qa-workflowLambdaRole2"},
{"Fn::Sub": "arn:aws:iam::role/orderserv-qa-workflowLmbdRole3"}
],
"Effect": "Allow"
}
]
}
}
]
}
}
}
}

Parameterize the pass role ARNs and have different set of parameter files for each environment
And, since you have multiple values, use CommaDelimitedList type parameter which can take multiple string values
Ref here: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/parameters-section-structure.html

Related

How to run AWS ECS Task with CloudFormation overriding container environment variables

I was searching a way to run ecs task. I already have a cluster and task definition settings. I just wanted to trigger a task using CloudFormation template. I know that I can run a task by clicking on the console and it works fine. For cfn, approach needs to be define properly.
Check the attached screenshots. I wanted to run that task using CloudFormation and pass container override environment variables. As per my current templates, it is not allowing me to do same like I can do using console. Using console I just need to select the following options
1. Launch type
2. Task Definition
Family
Revision
3. VPC and security groups
4. Environment variable overrides rest of the things automatically selected
It starts working with console but with cloudformaton template how can we do that. Is it possible to do or there is no such feature?
"taskdefinition": {
"Type" : "AWS::ECS::TaskDefinition",
"DependsOn": "DatabaseMaster",
"Properties" : {
"ContainerDefinitions" : [{
"Environment" : [
{
"Name" : "TARGET_DATABASE",
"Value" : {"Ref":"DBName"}
},
{
"Name" : "TARGET_HOST",
"Value" : {"Fn::GetAtt": ["DatabaseMaster", "Endpoint.Address"]}
}
]
}],
"ExecutionRoleArn" : "arn:aws:iam::xxxxxxxxxx:role/ecsTaskExecutionRole",
"Family" : "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"TaskRoleArn" : "arn:aws:iam::xxxxxxxxxxxxxxx:role/xxxxxxxxxxxxxxx-XXXXXXXXX"
}
},
"EcsService": {
"Type" : "AWS::ECS::Service",
"Properties" : {
"Cluster" : "xxxxxxxxxxxxxxxxx",
"LaunchType" : "FARGATE",
"NetworkConfiguration" : {
"AwsvpcConfiguration" : {
"SecurityGroups" : ["sg-xxxxxxxxxxx"],
"Subnets" : ["subnet-xxxxxxxxxxxxxx"]
}
},
"TaskDefinition" : "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}
}
There is no validity error in the code however, I am talking about the approach. I added image name container name but now it is asking for memory and cpu, it should not ask as it is already defined we just need to run a task.
Edited
I wanted to run a task after creation of my database and wanted to pass those database values to the task to run and complete a job.
Here is the working example of what you can do if you wanted to pass variable and run a task. In my case, I wanted to run a task after creation of my database but with environment variables, directly AWS does not provide any feature to do so, this is the solution which can help to trigger you ecs task.
"IAMRole": {
"Type": "AWS::IAM::Role",
"Properties": {
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": [
"events.amazonaws.com"
]
},
"Action": [
"sts:AssumeRole"
]
}
]
},
"Description": "Allow CloudWatch Events to trigger ECS task",
"Policies": [
{
"PolicyName": "Allow-ECS-Access",
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ecs:*",
"iam:PassRole",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "*"
}
]
}
}
],
"RoleName": { "Fn::Join": [ "", ["CloudWatchTriggerECSRole-", { "Ref": "DBInstanceIdentifier" }]]}
}
},
"DummyParameter": {
"Type" : "AWS::SSM::Parameter",
"Properties" : {
"Name" : {"Fn::Sub": "${AWS::StackName}-${DatabaseMaster}-EndpointAddress"},
"Type" : "String",
"Value" : {"Fn::GetAtt": "DatabaseMaster.Endpoint.Address"}
},
"DependsOn": "TaskSchedule"
},
"TaskSchedule": {
"Type": "AWS::Events::Rule",
"Properties": {
"Description": "Trigger ECS task upon creation of DB instance",
"Name": { "Fn::Join": [ "", ["ECSTaskTrigger-", { "Ref": "DBName" }]]},
"RoleArn": {"Fn::GetAtt": "IAMRole.Arn"},
"EventPattern": {
"source": [ "aws.ssm" ],
"detail-type": ["Parameter Store Change"] ,
"resources": [{"Fn::Sub":"arn:aws:ssm:eu-west-1:XXXXXXX:parameter/${AWS::StackName}-${DatabaseMaster}-EndpointAddress"}],
"detail": {
"operation": ["Create"],
"name": [{"Fn::Sub": "${AWS::StackName}-${DatabaseMaster}-EndpointAddress"}],
"type": ["String"]
}
},
"State": "ENABLED",
"Targets": [
{
"Arn": "arn:aws:ecs:eu-west-1:xxxxxxxx:cluster/NameOf-demo",
"Id": "NameOf-demo",
"RoleArn": {"Fn::GetAtt": "IAMRole.Arn"},
"EcsParameters": {
"LaunchType": "FARGATE",
"NetworkConfiguration": {
"AwsVpcConfiguration": {
"SecurityGroups": {"Ref":"VPCSecurityGroups"},
"Subnets": {"Ref":"DBSubnetName"}
}
},
"PlatformVersion": "LATEST",
"TaskDefinitionArn": "arn:aws:ecs:eu-west-1:XXXXXXXX:task-definition/NameXXXXXXXXX:1"
},
"Input": {"Fn::Sub": [
"{\"containerOverrides\":[{\"name\":\"MyContainerName\",\"environment\":[{\"name\":\"VAR1\",\"value\":\"${TargetDatabase}\"},{\"name\":\"VAR2\",\"value\":\"${TargetHost}\"},{\"name\":\"VAR3\",\"value\":\"${TargetHostPassword}\"},{\"name\":\"VAR4\",\"value\":\"${TargetPort}\"},{\"name\":\"VAR5\",\"value\":\"${TargetUser}\"},{\"name\":\"VAR6\",\"value\":\"${TargetLocation}\"},{\"name\":\"VAR7\",\"value\":\"${TargetRegion}\"}]}]}",
{
"VAR1": {"Ref":"DBName"},
"VAR2": {"Fn::GetAtt": ["DatabaseMaster", "Endpoint.Address"]},
"VAR3": {"Ref":"DBPassword"},
"VAR4": "5432",
"VAR5": {"Ref":"DBUser"},
"VAR6": "value6",
"VAR7": "eu-west-2"
}
]}
}
]
}
}
For Fargate task, we need to specify in CPU in Task Definition. and memory or memory reservation in either task or container definition.
and environment variables should be passed to each container as ContainerDefinitions and overrided when task is run from ecs task-run from console or cli.
{
"ContainerTaskdefinition": {
"Type": "AWS::ECS::TaskDefinition",
"Properties": {
"Family": "SomeFamily",
"ExecutionRoleArn": !Ref RoleArn,
"TaskRoleArn": !Ref TaskRoleArn,
"Cpu": "256",
"Memory": "1GB",
"NetworkMode": "awsvpc",
"RequiresCompatibilities": [
"EC2",
"FARGATE"
],
"ContainerDefinitions": [
{
"Name": "container name",
"Cpu": 256,
"Essential": "true",
"Image": !Ref EcsImage,
"Memory": "1024",
"LogConfiguration": {
"LogDriver": "awslogs",
"Options": {
"awslogs-group": null,
"awslogs-region": null,
"awslogs-stream-prefix": "ecs"
}
},
"Environment": [
{
"Name": "ENV_ONE_KEY",
"Value": "Valu1"
},
{
"Name": "ENV_TWO_KEY",
"Value": "Valu2"
}
]
}
]
}
}
}
EDIT(from discussion in comments):
ECS Task Run is not a cloud-formation resource, it can only be run from console or CLI.
But if we choose to run from a cloudformation resource, it can be done using cloudformation custom resource. But once task ends, we now have a resource in cloudformation without an actual resource behind. So, custom resource needs to do:
on create: run the task.
on delete: do nothing.
on update: re-run the task
Force an update by changing an attribute or logical id, every time we need to run the task.

Cache images received from Firebase Storage in PWA application

I have an application in Angular with PWA configured, besides caching assets/images I would also like to cache the images that are in Firebase Storage once they are loaded when I am Online.
My application makes use of the Cloud Firestore database with data persistence enabled. When I need to load the avatar of the authenticated user on the system in offline mode, it tries to load through the photoURL field, but since it is offline I can not load the image so the image is not displayed and this is not legal for the user.
In my code I load the image as follows:
<img class="avatar mr-0 mr-sm-16" src="{{ (user$ | async)?.photoURL || 'assets/images/avatars/profile.svg' }}">
I would like it when it was offline, it would search somewhere in the cache for the image that was uploaded.
It would be very annoying every time I load the images to call some method to store the cached image or something, I know it is possible but I do not know how to do that.
Is it possible to do this through the ngsw-config.json configuration file?
ngsw-config.json:
{
"index": "/index.html",
"assetGroups": [
{
"name": "app",
"installMode": "prefetch",
"resources": {
"files": [
"/favicon.ico",
"/index.html",
"/*.css",
"/*.js"
],
"urls": [
"https://fonts.googleapis.com/css?family=Muli:300,400,600,700"
]
}
}, {
"name": "assets",
"installMode": "lazy",
"updateMode": "prefetch",
"resources": {
"files": [
"/assets/**",
"/*.(eot|svg|cur|jpg|png|webp|gif|otf|ttf|woff|woff2|ani)"
]
}
}
]
}
Yes, it's possible, I tried and works for me, I have a pwa with ionic and angular 7, in my 'ngsw-config.json' I used this config:
{
"index": "/index.html",
"assetGroups": [{
"name": "app",
"installMode": "prefetch",
"resources": {
"files": [
"/favicon.ico",
"/index.html",
"/*.css",
"/*.js"
]
}
}, {
"name": "assets",
"installMode": "lazy",
"updateMode": "prefetch",
"resources": {
"files": [
"/assets/**",
"/*.(eot|svg|cur|jpg|png|webp|gif|otf|ttf|woff|woff2|ani)"
]
}
}],
"dataGroups": [{
"name": "api-freshness",
"urls": [
"https://firebasestorage.googleapis.com/v0/b/mysuperrpwapp.appspot.com/"
],
"cacheConfig": {
"maxSize": 100,
"maxAge": "180d",
"timeout": "10s",
"strategy": "freshness"
}
}]
}
In this article is well explained how works and what strategies you can use.
https://medium.com/progressive-web-apps/a-new-angular-service-worker-creating-automatic-progressive-web-apps-part-1-theory-37d7d7647cc7
It was very important in testing to have a valid https connection for the 'service_worker' starts. Once get offline, you can see that the file comes from "service_worker"
Test img _ from service_worker
just do
storage.ref("pics/yourimage.jpg").updateMetatdata({ 'cacheControl': 'private, max-age=15552000' }).subscribe(e=>{ });
and in your ngsw-config.json
"assetGroups": [{
"name": "app",
"installMode": "prefetch",
"resources": {
"files": [
"/favicon.ico",
"/index.html",
"/*.css",
"/*.js"
],
"url":[
"https://firebasestorage.googleapis.com/v0/b/*"
]
}
}

AWS IAM Policy To Restrict S3 Access (Prefix) Based On IAM User's Tag

A variety of IAM users are sharing access to an S3 bucket. The S3 bucket has content separated by user so each user has a unique area they have access to.
For instance:
S3 Bucket: example-bucket.
IAM User: UserOne. This user is tagged with sampleTag=u11111.
IAM User: UserTwo. This user is tagged with sampleTag=u22222.
Many more tagged IAM users.
I'd like to write an IAM policy such that:
UserOne has access to read+write content to s3://example-bucket/u11111/* and read content from s3://example-bucket/config/u11111/
UserTwo has access to read+write content to s3://example-bucket/u22222/* and read content from s3://example-bucket/config/u22222/
Etc...
Note that the S3 key includes the value of the sampleTag in the path.
I'd like this single policy to be able to be applied to the entire group of IAM users without need to include an individual policy for each user.
I expected this to be possible thanks to ${aws:PrincipalTag/sampleTag} which I thought would inject the tag value in that location in the resource strings. But after playing with the policy simulator, it doesn't seem to accomplish this.
Current policy looks like:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:ListAllMyBuckets"
],
"Resource": "arn:aws:s3:::*"
},
{
"Effect": "Allow",
"Action": [
"s3:ListBucket",
"s3:GetBucketLocation"
],
"Resource": [
"arn:aws:s3:::example-bucket",
"arn:aws:s3:::example-bucket-test"
]
},
{
"Effect": "Allow",
"Action": [
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::example-bucket",
"arn:aws:s3:::example-bucket-test"
],
"Condition": {
"StringEquals": {
"s3:prefix": [
"",
"${aws:PrincipalTag/sampleTag}/"
],
"s3:delimiter": ["/"]
}
}
},
{
"Effect": "Allow",
"Action": [
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::example-bucket",
"arn:aws:s3:::example-bucket-test"
],
"Condition": {
"StringLike": {
"s3:prefix": ["${aws:PrincipalTag/sampleTag}/*"]
}
}
},
{
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:GetObjectAcl",
"s3:GetObjectVersion",
"s3:GetObjectVersionAcl"
],
"Resource": [
"arn:aws:s3:::example-bucket/config/${aws:PrincipalTag/sampleTag}/*",
"arn:aws:s3:::example-bucket-test/config/${aws:PrincipalTag/sampleTag}/*"
]
},
{
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:GetObject",
"s3:GetObjectAcl",
"s3:GetObjectVersion",
"s3:GetObjectVersionAcl",
"s3:DeleteObject"
],
"Resource": [
"arn:aws:s3:::example-bucket/${aws:PrincipalTag/sampleTag}/*",
"arn:aws:s3:::example-bucket-test/${aws:PrincipalTag/sampleTag}/*"
]
}
]
}
I think the last two policies to not work. I can't find documentation to say if you can embed variables into the Resource strings or not, but s3:prefix doesn't seem to be available for GetObject or PutObject operations -- so I'm not sure how else to restrict the scope of those permissions.
Any ideas as to what is wrong or how to accomplish this would be appreciated!
I think it is possible to use ${aws:PrincipalTag} in the Ressource property of a policy. Just look into the docs of IAM. The example uses the PrincipalTag as last part of the Ressource value.
(Just use Strg + F and type PrincipalTag on the docs website an you find the example)
You are right, the last two policies will not work.
According to documentation aws:PrincipalTag/tag-key works with string operators, hence aws:PrincipalTag/tag-key usage will only works inside Condition policy elements.
Also, s3:prefix condition key only work for ListBucket & ListBucketVersions actions: https://docs.aws.amazon.com/IAM/latest/UserGuide/list_amazons3.html#amazons3-policy-keys
You can have PrincipalTag as part of the resource.
This works.
{
"Sid": "FullPermissionOnlyForPrefix",
"Effect": "Allow",
"Action": "s3:*",
"Resource": [
"arn:aws:s3:::mybucket/${aws:PrincipalTag/team}*",
"arn:aws:s3:::mybucket/${aws:PrincipalTag/team}/*"
]
}

Specify shared/'common' values for configurations in CppProperties.json or CMakeSettings.json

When using the "Open Folder" functionality of Visual Studio, the IDE searches for project settings and configurations in a special json file. For CPP projects, this could be CppProperties.json. For CMake projects, this could be CMakeSettings.json.
This json file contains a collection of one or more "configurations," such as "Debug" or "Release". I will use a recent CMake project as an example:
"configurations": [
{
"name": "ARM-Debug",
"generator": "Ninja",
"configurationType": "Debug",
"inheritEnvironments": [
"gcc-arm"
],
"buildRoot": "${env.USERPROFILE}\\CMakeBuilds\\${workspaceHash}\\build\\${name}",
"installRoot": "${env.USERPROFILE}\\CMakeBuilds\\${workspaceHash}\\install\\${name}",
"cmakeCommandArgs": "",
"buildCommandArgs": "-v",
"ctestCommandArgs": "",
"intelliSenseMode": "linux-gcc-arm",
"variables": [
{
"name": "CMAKE_TOOLCHAIN_FILE",
"value": "${workspaceRoot}/cmake/arm-none-eabi-toolchain.cmake"
}
]
},
{
"name": "ARM-Release",
"generator": "Ninja",
"configurationType": "Release",
"inheritEnvironments": [
"gcc-arm"
],
"buildRoot": "${env.USERPROFILE}\\CMakeBuilds\\${workspaceHash}\\build\\${name}",
"installRoot": "${env.USERPROFILE}\\CMakeBuilds\\${workspaceHash}\\install\\${name}",
"cmakeCommandArgs": "",
"buildCommandArgs": "-v",
"ctestCommandArgs": "",
"intelliSenseMode": "linux-gcc-arm",
"variables": [
{
"name": "CMAKE_TOOLCHAIN_FILE",
"value": "${workspaceRoot}/cmake/arm-none-eabi-toolchain.cmake"
}
]
}
As you can see, I have two configurations with nearly identical properties.
My question: is it possible to define these common/shared properties once, in such a way as to allow the configurations to inherit them and avoid repeating myself?
The easier way is to define an environment at global level (outside of any configuration), such as:
{
"environments": [
{
"namespace" : "env",
"varName": "varValue"
}
],
Then you can reuse that wherever you need to, e.g.:
"cmakeCommandArgs": "${env.varName}",
You can also have multiple environments, and reuse them, like this:
{
"environments": [
{
"environment": "env1",
"namespace": "env",
"varName": "varValueEnv1"
},
{
"environment": "env2",
"namespace": "env",
"varName": "varValueEnv2"
}
],
"configurations": [
{
"name": "x64-Release",
"inheritEnvironments": [
"msvc_x64_x64", "env2"
],
"cmakeCommandArgs": "${env.varName}",
.....
}
]
the 'x64-Release' will inherit the variables's value in the environment called "env2" (namespace 'env')

Addition of subnets to existing Vnet through ARM templates

The following is my input parameter file(parameter.json)
{
"VNetSettings":{
"value":{
"name":"VNet2",
"addressPrefixes":"10.0.0.0/16",
"subnets":[
{
"name": "sub1",
"addressPrefix": "10.0.1.0/24"
},
{
"name":"sub2",
"addressPrefix":"10.0.2.0/24"
}
]
}
}
}
The following is my arm template that should deploy the subnets.(deploy.json)
{
"contentversion":"1.0.0.0",
"$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"parameters":{
"VNetSettings":{"type":"object"},
"noofsubnets":
{
"type":"int"
},
"newOrExisting":
{
"type":"string",
"allowedvalues":
[
"new",
"existing"
]
}
},
"resources":
[{
"condition":"[equals(parameters('newOrExisting'),'new')]",
"type": "Microsoft.Network/virtualNetworks",
"mode":"Incremental",
"apiVersion": "2015-06-15",
"name":"[parameters('VNetSettings').name]",
"location":"[resourceGroup().location]",
"properties":
{
"addressSpace":{
"addressPrefixes":["[parameters('VNetSettings').addressPrefixes]"]
},
"copy":
[{
"name":"subnets",
"count":"[parameters('noofsubnets')]",
"input":
{
"name": "[parameters('VNetSettings').subnets[copyIndex('subnets')].name]",
"properties":
{
"addressPrefix": "[parameters('VNetSettings').subnets[copyIndex('subnets')].addressPrefix]"
}
}
}]
}
}]
}
What the template should be doing is add these two subnets(sub1 & sub2) to the Vnet in addition to the existing subnets if there is already one.But what it is doing is replacing the existing subnets with these two subnets present in the input file. Mode: Incremental should be doing this but I'm not sure whether I'm placing it in the right place. I'm deploying this template using the following powershell commmand:
New-AzureRmResourceGroupDeployment -Name testing -ResourceGroupName rgname -TemplateFile C:\Test\deploy.json -TemplateParameterFile C:\Test\parameterfile.json
This is expected behavior. you should read on 'Idempotence'. what you need to do is create a subnet resource, that way you will work around it.
{
"apiVersion": "2016-03-30",
"name": "vnetName\subnetName",
"location": "[resourceGroup().location]]",
"type": "Microsoft.Network/virtualNetworks/subnets",
"properties": {
"addressPrefix": "xx.x.x.xx"
}
}
vnetName has to be the vnet you want to create resource in.