Last active
May 12, 2020 03:43
-
-
Save pitbulk/2b4b27b647ad332a2b5bc13f0c104295 to your computer and use it in GitHub Desktop.
StackSet template for deploying Onelogin with AWS Control Tower
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
AWSTemplateFormatVersion: 2010-09-09 | |
Description: Create OneLogin IDP, Role to allow OL extract account role list and default roles on all accounts. | |
Metadata: | |
AWS::CloudFormation::Interface: | |
ParameterGroups: | |
- | |
Label: | |
default: "OneLogin SAML IDP" | |
Parameters: | |
- OLMetadataUrl | |
- OLExternalId | |
- | |
Label: | |
default: "Roles" | |
Parameters: | |
- EnableAdminRole | |
- AdminRoleName | |
- EnablePowerUserRole | |
- PowerUserRoleName | |
- EnableReadOnlyRole | |
- ReadOnlyRoleName | |
Mappings: | |
OLMapping: | |
IDPName: | |
Name: OLIdP | |
RoleName: | |
Name: OLGetRoles | |
Parameters: | |
OLMetadataUrl: | |
Type: String | |
Description: Publicly accessible HTTPS location where SAML metadata.xml can be downloaded. | |
OLExternalId: | |
Type: String | |
Description: (Optional) OneLogin External ID generated at the OL App Configuration tab used to in. Used in the Onelogin integration that extracts the roles from AWS. | |
AdminRoleName: | |
Type: String | |
Description: Role name for administrator access. | |
Default: AdministratorAccessRole | |
PowerUserRoleName: | |
Type: String | |
Description: Role name for administaror but IAM access. | |
Default: PowerUserAccessRole | |
ReadOnlyRoleName: | |
Type: String | |
Description: Role name for read-only access. | |
Default: ReadOnlyAccessRole | |
EnableAdminRole: | |
Type: String | |
Default: 'true' | |
Description: Create an administrative role. | |
AllowedValues: | |
- 'true' | |
- 'false' | |
EnablePowerUserRole: | |
Type: String | |
Default: 'true' | |
Description: Create an administrative but IAM role. | |
AllowedValues: | |
- 'true' | |
- 'false' | |
EnableReadOnlyRole: | |
Type: String | |
Default: 'true' | |
Description: Create a read-only role. | |
AllowedValues: | |
- 'true' | |
- 'false' | |
Conditions: | |
CreateAdminRole: !Equals | |
- !Ref EnableAdminRole | |
- 'true' | |
CreatePowerUserRole: !Equals | |
- !Ref EnablePowerUserRole | |
- 'true' | |
CreateReadOnlyRole: !Equals | |
- !Ref EnableReadOnlyRole | |
- 'true' | |
Resources: | |
AdminRole: | |
Type: AWS::IAM::Role | |
Condition: CreateAdminRole | |
Properties: | |
RoleName: !Ref AdminRoleName | |
AssumeRolePolicyDocument: | |
Version: 2012-10-17 | |
Statement: | |
- Effect: Allow | |
Principal: | |
Federated: | |
- !GetAtt SAMLIdp.SAMLProviderArn | |
Action: | |
- sts:AssumeRoleWithSAML | |
Condition: | |
StringEquals: | |
'SAML:aud': | |
'https://signin.aws.amazon.com/saml' | |
Path: / | |
ManagedPolicyArns: | |
- arn:aws:iam::aws:policy/AdministratorAccess | |
PowerUserRole: | |
Type: AWS::IAM::Role | |
Condition: CreatePowerUserRole | |
Properties: | |
RoleName: !Ref PowerUserRoleName | |
AssumeRolePolicyDocument: | |
Version: 2012-10-17 | |
Statement: | |
- Effect: Allow | |
Principal: | |
Federated: | |
- !GetAtt SAMLIdp.SAMLProviderArn | |
Action: | |
- sts:AssumeRoleWithSAML | |
Condition: | |
StringEquals: | |
'SAML:aud': | |
'https://signin.aws.amazon.com/saml' | |
Path: / | |
ManagedPolicyArns: | |
- arn:aws:iam::aws:policy/PowerUserAccess | |
ReadOnlyRole: | |
Type: AWS::IAM::Role | |
Condition: CreateReadOnlyRole | |
Properties: | |
RoleName: !Ref ReadOnlyRoleName | |
AssumeRolePolicyDocument: | |
Version: 2012-10-17 | |
Statement: | |
- Effect: Allow | |
Principal: | |
Federated: | |
- !GetAtt SAMLIdp.SAMLProviderArn | |
Action: | |
- sts:AssumeRoleWithSAML | |
Condition: | |
StringEquals: | |
'SAML:aud': | |
'https://signin.aws.amazon.com/saml' | |
Path: / | |
ManagedPolicyArns: | |
- arn:aws:iam::aws:policy/ReadOnlyAccess | |
OLListRolesRole: | |
Type: AWS::IAM::Role | |
Properties: | |
RoleName: !FindInMap [OLMapping, "RoleName", "Name"] | |
Policies: | |
- PolicyName: listroles | |
PolicyDocument: | |
Statement: | |
- Effect: Allow | |
Action: | |
- iam:ListRoles | |
- iam:ListAccountAliases | |
Resource: '*' | |
AssumeRolePolicyDocument: | |
Version: 2012-10-17 | |
Statement: | |
- Effect: Allow | |
Principal: | |
AWS: 842984801698 | |
Action: | |
- sts:AssumeRole | |
Condition: | |
StringEquals: | |
'sts:ExternalId': | |
!Ref OLExternalId | |
Path: / | |
SAMLIdpLambdaRole: | |
Type: 'AWS::IAM::Role' | |
Properties: | |
AssumeRolePolicyDocument: | |
Version: '2012-10-17' | |
Statement: | |
- Effect: Allow | |
Principal: | |
Service: 'lambda.amazonaws.com' | |
Action: | |
- 'sts:AssumeRole' | |
Path: '/' | |
ManagedPolicyArns: | |
- 'arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole' | |
Policies: | |
- PolicyName: createupdate | |
PolicyDocument: | |
Statement: | |
- Effect: Allow | |
Action: | |
- 'iam:CreateSAMLProvider' | |
- 'iam:UpdateSAMLProvider' | |
- 'iam:DeleteSAMLProvider' | |
Resource: !Join | |
- '' | |
- - 'arn:aws:iam::*:saml-provider/' | |
- !FindInMap [OLMapping, "IDPName", "Name"] | |
- PolicyName: list | |
PolicyDocument: | |
Statement: | |
- Effect: Allow | |
Action: | |
- 'iam:ListSAMLProviders' | |
Resource: '*' | |
SAMLIdpLambda: | |
Type: "AWS::Lambda::Function" | |
Properties: | |
Handler: "index.handler" | |
Role: | |
Fn::GetAtt: | |
- "SAMLIdpLambdaRole" | |
- "Arn" | |
Code: | |
ZipFile: > | |
var cfr = require('./cfn-response'); | |
var AWS = require('aws-sdk'); | |
var iam = new AWS.IAM(); | |
var https = require('https'); | |
var fs = require('fs'); | |
exports.handler = function(event, context) { | |
console.log(event); | |
var resID = event.PhysicalResourceId || event.RequestId; | |
var metadataUrl = event.ResourceProperties.metadataUrl; | |
var idpName = event.ResourceProperties.idpName; | |
var filename = '/tmp/metadata.xml' | |
var file = fs.createWriteStream(filename); | |
if (event.RequestType == 'Create' || event.RequestType == 'Update') { | |
var request = https.get(metadataUrl, function(response) { | |
response.pipe(file); | |
file.on('finish', function() { | |
file.close( function () { | |
let rawdata = fs.readFileSync(filename, 'utf8'); | |
if (event.RequestType == 'Update') { | |
var paramslist = {}; | |
iam.listSAMLProviders(paramslist, function(err, data) { | |
if (err) cfr.send(event, context, cfr.FAILED, err, resID); | |
else { | |
var arn=''; | |
for (var provider in data.SAMLProviderList) { | |
if (data.SAMLProviderList[provider].Arn.split('/')[1] == idpName) { | |
console.log('Update provider. ' + data.SAMLProviderList[provider].Arn + ' already exists.') | |
arn = data.SAMLProviderList[provider].Arn; | |
} | |
} | |
if (arn == '') { | |
var params = { Name: idpName, SAMLMetadataDocument: rawdata }; | |
iam.createSAMLProvider(params, function(err, data) { | |
if (err) cfr.send(event, context, cfr.FAILED, err, resID); | |
else cfr.send(event, context, cfr.SUCCESS, data, resID); | |
}); | |
} else { | |
var params = { SAMLProviderArn: arn, SAMLMetadataDocument: rawdata }; | |
iam.updateSAMLProvider(params, function(err, data) { | |
if (err) cfr.send(event, context, cfr.FAILED, err, resID); | |
else cfr.send(event, context, cfr.SUCCESS, data, resID); | |
}); | |
} | |
} | |
}); | |
} else { | |
var params = { Name: idpName, SAMLMetadataDocument: rawdata }; | |
iam.createSAMLProvider(params, function(err, data) { | |
if (err) cfr.send(event, context, cfr.FAILED, err, resID); | |
else cfr.send(event, context, cfr.SUCCESS, data, resID); | |
}); | |
} | |
}); | |
}); | |
}).on('error', function(err) { | |
fs.unlink(filename); | |
cfr.send(event, context, cfr.FAILED, err); | |
}); | |
} else if (event.RequestType == 'Delete') { | |
var paramslist = {}; | |
iam.listSAMLProviders(paramslist, function(err, data) { | |
if (err) cfr.send(event, context, cfr.FAILED, err, resID); | |
else { | |
var deleteResource = false; | |
var arn; | |
console.log(data.SAMLProviderList); | |
for (var provider in data.SAMLProviderList) { | |
if (data.SAMLProviderList[provider].Arn.split('/')[1] == idpName) { | |
console.log('Delete provider. ' + data.SAMLProviderList[provider].Arn + ' already exists.') | |
deleteResource = true; | |
arn = data.SAMLProviderList[provider].Arn; | |
} | |
} | |
var params = { SAMLProviderArn: arn }; | |
if (deleteResource == true) { | |
iam.deleteSAMLProvider(params, function(err, data) { | |
if (err) cfr.send(event, context, cfr.FAILED, err, resID); | |
else cfr.send(event, context, cfr.SUCCESS, data, resID); | |
}); | |
} else { | |
cfr.send(event, context, cfr.SUCCESS, {success: 'Provider did not exist'}, resID); | |
} | |
} | |
}); | |
} else { | |
cfr.send(event, context, cfr.FAILED, { error: 'Method not supported'}, resID); | |
} | |
}; | |
Runtime: "nodejs10.x" | |
Timeout: "30" | |
SAMLIdp: | |
Type: Custom::SAMLIdp | |
Properties: | |
ServiceToken: !GetAtt SAMLIdpLambda.Arn | |
metadataUrl: !Ref OLMetadataUrl | |
idpName: !FindInMap [OLMapping, "IDPName", "Name"] |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Can you update line #314 and set runtime to nodejs10.x ?
CloudFormation takes nodejs10.x as runtime instead of nodejs10.13
ref: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-function.html
There are also a few libraries load error when I tried to run it using nodejs10.x, perhaps you could zip the Lambda with all dependencies into S3 perhaps?
ref: https://docs.aws.amazon.com/lambda/latest/dg/nodejs-create-deployment-pkg.html#nodejs-package-dependencies